图论专题总结
Posted xingmi-weiyouni
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了图论专题总结相关的知识,希望对你有一定的参考价值。
T1菜肴制作:拓扑排序+大根堆
卡了好一会儿才过掉。正序拓扑的话贪心策略会出错。
保证先输出小的,倒序拓扑保证先搞大的。然后插到大根堆里。
每次取出最大的(堆顶)进行拓扑扩展。pop出来的直接扔进栈里。
多判有点恶心。记得清空(我就因为tot没清空,样例第三组单测正确,多测就错。。)
还有一个特殊判断:栈内元素个数与本身元素个数是否相符。
不相符就是剩下了环搞不出来,impossible就行了。
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<queue> #include<stack> #define rint register int using namespace std; int T,n,m,a,b,tot,first[100003]; int du[100003]; struct node int u,v,nxt; ; bool pan=0,vis[100003]; inline void add(int uu,int vv,node edge[]) ++tot; edge[tot].u=uu; edge[tot].v=vv; edge[tot].nxt=first[uu]; first[uu]=tot; int main() scanf("%d",&T); while(T--) scanf("%d %d",&n,&m); memset(vis,0,sizeof(vis)); memset(du,0,sizeof(du)); memset(first,0,sizeof(first)); tot=0; priority_queue <int> q; stack <int> s; node edge[100003]; for(rint i=1;i<=m;++i) scanf("%d %d",&a,&b); add(b,a,edge);du[a]++; for(rint i=1;i<=n;++i) if(!du[i])q.push(i); vis[i]=1; if(q.empty())cout<<"Impossible!"<<endl;continue; while(!q.empty()) int xx=q.top();s.push(xx);q.pop(); // cout<<xx<<endl; pan=0; for(rint i=first[xx];i;i=edge[i].nxt) int yy=edge[i].v; du[yy]--; if(!du[yy]) pan=1; q.push(yy); // cout<<"yy"<<yy<<endl; vis[yy]=1; // if(pan==0&&q.empty())cout<<"Impossible!"<<endl;break; // if(pan==0)continue; if(s.size()!=n)cout<<"Impossible!"<<endl;continue; while(!s.empty()) cout<<s.top()<<‘ ‘; s.pop(); cout<<endl;
T2矩阵游戏:二分图
锝瑟一把这题全hzoj我是第一个A掉的
二分图,一开始还真没想起来。查bzoj上这道题的时候撇了一眼看到了二分图三个字。
好的。(不要脸)但是真的忘了啊QAQ只好翻出ftp里尘封多年的二分图ppt。
手码了一遍匈牙利感觉还不错。
挺基础,行列分别建点,黑格建边,跑一边匈牙利,用黑格去匹配行和列。
然而没认真看题,人家让输出“Yes”或“No”,我输出“YES”“NO”完美w0。。。
加了一堆卡常特盘,148毫算是挺快的了吧?(wtf 102ms!收小的一拜)
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #include<cmath> #include<algorithm> using namespace std; const int N=205; int a[N][N]; int vis[N],matx[N],maty[N]; int tot; int n; int hun(int x) for(int i=1;i<=n;i++) //int y=to[i]; if(!vis[i]&&a[x][i]) vis[i]=1; if(maty[i]==-1||hun(maty[i])) maty[i]=x; matx[x]=i; return 1; return 0; int main() int T; scanf("%d",&T); while(T--) tot=0; memset(maty,-1,sizeof(maty)); memset(matx,-1,sizeof(matx)); scanf("%d",&n); int cnt=0; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%d",&a[i][j]); int ans=0; for(int i=1;i<=n;i++) memset(vis,0,sizeof(vis)); if(hun(i)) ans++; //cout<<ans<<endl; if(ans!=n) printf("No\\n"); else puts("Yes\\n");
T3约会 Rendezvous:基环树
为什么基环树的题目都这么恶心还是我太弱鸡了?
我可能卡了一年。自己想出来的代码5.0k,198行……我真的没勇气调下去了。
记录了一大堆东西。判断的语句写了一堆。。真的是堆QAQ。
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<queue> #include<vector> #define rint register int const int L=1<<20|1; char buffer[L],*S,*T; #define getchar() ((S==T&&(T=(S=buffer)+fread(buffer,1,L,stdin),S==T))?EOF:*S++) using namespace std; int n,m,head[500005],tot,fat[500005]; int dex,cnt,t,bar[500005],bl[500005],ac[500005]; int d[500005],f[500005][20],l_a[500005],l_b[500005]; vector<int>dell[500005];queue<int>q; struct nodeint to,nxt;edge[500005]; int read() rint a=0,b=1;char ch=getchar(); while(ch<‘0‘||ch>‘9‘)if(ch==‘-‘)b=-1;ch=getchar(); while(ch>=‘0‘&&ch<=‘9‘)a=a*10+ch-‘0‘;ch=getchar(); return a*b; void add(rint x,rint y) edge[++tot].to=y; edge[tot].nxt=head[x]; head[x]=tot; int find(rint x) if(fat[x]==x) return x; return fat[x]=find(fat[x]); void get_dell(rint x,rint dex) bar[x]=dex; for(rint i=head[x];i;i=edge[i].nxt) rint y=edge[i].to; if(bar[y]==dex) ++cnt; while(y!=x) dell[cnt].push_back(x); x=ac[x]; dell[cnt].push_back(y); else ac[y]=x,get_dell(y,dex); void bfs(rint x,rint tp) d[x]=1,bl[x]=tp; q.push(x); while(!q.empty()) rint x=q.front(); q.pop(); for(rint i=head[x];i;i=edge[i].nxt) rint y=edge[i].to; if(!bar[y]&&!d[y]) bl[y]=tp;d[y]=d[x]+1; q.push(y);f[y][0]=x; for(rint j=1;j<=t;j++) f[y][j]=f[f[y][j-1]][j-1]; int Lca(rint x,rint y) if(d[x]>d[y]) swap(x,y); for(rint i=t;i>=0;i--) if(d[f[y][i]]>=d[x]) y=f[y][i]; if(x==y) return x; for(rint i=t;i>=0;i--) if(f[y][i]^f[x][i]) y=f[y][i]; x=f[x][i]; return f[x][0]; void solve() for(rint i=1;i<=n;i++) if(!bar[i]) get_dell(i,++dex); memset(bar,0,sizeof(bar)); for(rint i=1;i<=cnt;i++) for(rint j=0;j<dell[i].size();j++) bar[dell[i][j]]=1,l_a[dell[i][j]]=j,l_b[dell[i][j]]=i; for(rint i=1;i<=cnt;i++) for(rint j=0;j<dell[i].size();j++) bfs(dell[i][j],dell[i][j]); int main() n=read(),m=read(); t=log2(n)+1; for(rint i=1;i<=n;i++) fat[i]=i; for(rint i=1,x;i<=n;i++) x=read(); add(x,i); fat[find(i)]=find(x); solve(); for(rint i=1,x,y;i<=m;i++) x=read(),y=read(); if(find(x)!=find(y)) printf("-1 -1\\n"); else if(bl[x]==bl[y]) rint lca=Lca(x,y); rint ll=d[x]-d[lca],rr=d[y]-d[lca]; printf("%d %d\\n",ll,rr); else rint ll=abs(l_a[bl[x]]-l_a[bl[y]]),rr=dell[l_b[bl[x]]].size()-ll; rint edge=d[x]-d[bl[x]],r=d[y]-d[bl[y]]; rint ans1,ans2,ans3,ans4; if(l_a[bl[x]]<l_a[bl[y]]) ans1=edge+ll,ans2=r,ans3=edge,ans4=r+rr; else ans1=edge+rr,ans2=r,ans3=edge,ans4=r+ll; if(max(ans1,ans2)<max(ans3,ans4)) printf("%d %d\\n",ans1,ans2); else if(max(ans1,ans2)>max(ans3,ans4)) printf("%d %d\\n",ans3,ans4); else if(min(ans1,ans2)<min(ans3,ans4)) printf("%d %d\\n",ans1,ans2); else if(min(ans1,ans2)>min(ans3,ans4)) printf("%d %d\\n",ans3,ans4); else printf("%d %d\\n",max(ans1,ans2),min(ans1,ans2)); return 0;
T4tree:最小生成树+二分答案
被天皇忽悠说这道题贼恶心,去看题解(推锅推锅)然后1A掉不想说话。
不是特别好想。不过填了一种最小生成树题目的做题方法:通过改变边权来改变最小生成树的构成。
黑白边,要求need条白边。一开始sb的想成了树里只有白边。
显然还有n-1-need条黑边 /糊脸(n是节点数目)顺嘴吐槽:BZOJ上直接跑最小生成树都能过……
二分给白边加上边权,跑kruskal最小生成树,看是否符合need条白边。
直接暴力累计就好了没必要开优化(懒)还有人直接爆搜不二分都A了……
<-这是爆搜
<-这是二分
建议打二分咕咕咕~
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<queue> #include<stack> #include<algorithm> #define rint register int using namespace std; int n,m,need,l,r,cnt,tot,ans; int u[100005],v[100005],c[100005],col[100005]; int fa[100005]; struct node int u,v,c,col;edge[100005]; inline int read() int a=0,b=1;char ch=getchar(); while(ch<‘0‘||ch>‘9‘)if(ch==‘-‘)b=-1;ch=getchar(); while(ch>=‘0‘&&ch<=‘9‘)a=(a<<3)+(a<<1)+(ch-‘0‘);ch=getchar(); return a*b; inline bool cmp(node a,node b)return a.c==b.c?a.col<b.col:a.c<b.c; inline int get(int x)return x==fa[x]?x:fa[x]=get(fa[x]); inline bool kruskal(int x) int f1,f2,sum=0; for(rint i=1;i<=n;i++) fa[i]=i; for(rint i=1;i<=m;i++) edge[i].u=u[i]; edge[i].v=v[i]; edge[i].c=c[i]; edge[i].col=col[i]; if(!col[i]) edge[i].c+=x; sort(edge+1,edge+m+1,cmp); for(rint i=1;i<=m;i++) f1=get(edge[i].u),f2=get(edge[i].v); if(f1!=f2) fa[f1]=f2; sum++; tot+=edge[i].c; if(!edge[i].col) cnt++; if(sum==n-1) if(cnt>=need)return 1; else return 0; int main() n=read(),m=read(),need=read(); for(rint i=1;i<=m;i++) u[i]=read()+1,v[i]=read()+1,c[i]=read(),col[i]=read(); l=-101,r=101; while(l<r) tot=cnt=0; int mid=(l+r)>>1; if(kruskal(mid)) l=mid+1,ans=tot-need*mid; else r=mid; cout<<ans<<endl; return 0;
T5太鼓达人:dfs欧拉回路
颓了题解,膜拜soul神佬。
一看题真的想不出来哪里和图论有关系……查了一下全说打表……
坑
以上是关于图论专题总结的主要内容,如果未能解决你的问题,请参考以下文章