首页 > 代码库 > 强连通分量(学习心得)
强连通分量(学习心得)
定义:有向图强连通分量:在有向图G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通如果有向图G的每两个顶点都强连通,称G是一个强连通图。有向图的极大强连通子图,称为强连通分量。
求强连通分量:
vector<int>pic[maxn]; int dfn[maxn],low[maxn],ans[maxn]; bool ins[maxn]; stack<int>st; int dind=0,block=0; int siz[maxn],cud[maxn]; void tarjan(int x) { dind++; dfn[x]=low[x]=dind; ins[x]=true; st.push(x); for (int j=0;j<pic[x].size();j++) { int y=pic[x][j]; if (dfn[y]==0) { tarjan(y); low[x]=min(low[x],low[y]); } else if (ins[y]) low[x]=min(low[x],dfn[y]); } if (low[x]==dfn[x]) { block++; siz[block]=0; while (true) { int thi=st.top(); st.pop(); ans[thi]=block; siz[block]++; ins[thi]=false; if (thi==x) break; } } }
例题1【UVA-11324 最大团】
分析:先计算强连通分量,然后建一张新图,每个点的权值是它所包含的点的数量,求图的最长链。
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<vector> #include<stack> using namespace std; const int maxn=1010; vector<int> pic[maxn],new_[maxn]; int dfn[maxn],low[maxn],ans[maxn]; bool ins[maxn]; stack<int>st; int dind=0,block=0; int siz[maxn],dp[maxn],Ans; void tarjan(int x) { dind++; dfn[x]=low[x]=dind; ins[x]=true; st.push(x); for (int j=0;j<pic[x].size();j++) { int y=pic[x][j]; if (!dfn[y]){ tarjan(y); low[x]=min(low[x],low[y]); } else if (ins[y]) low[x]=min(low[x],dfn[y]); } if (low[x]==dfn[x]) { block++; siz[block]=0; while (true) { int thi=st.top(); st.pop(); ans[thi]=block; siz[block]++; ins[thi]=false; if (thi==x) break; } } } int DP(int x){ if(dp[x]!=-1) return dp[x]; int Max=0; for(int i=0;i<new_[x].size();i++) Max=max(Max,DP(new_[x][i])); return dp[x]=siz[x]+Max; } void init(int n){ for(int i=1;i<=n;i++) pic[i].clear(),new_[i].clear(); memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(ans,0,sizeof(ans)); memset(ins,0,sizeof(ins)); while(!st.empty()) st.pop(); dind=block=Ans=0; memset(siz,0,sizeof(siz)); memset(dp,-1,sizeof(dp)); } int main(){ int n,m,N,u,v; scanf("%d",&N); while(N--){ scanf("%d%d",&n,&m); init(n); while(m--){ scanf("%d%d",&u,&v); pic[u].push_back(v); } for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); for(int i=1;i<=n;i++) for(int j=0;j<pic[i].size();j++) if(ans[i]!=ans[pic[i][j]]) new_[ans[pic[i][j]]].push_back(ans[i]); for(int i=1;i<=block;i++) Ans=max(Ans,DP(i)); printf("%d\n",Ans); } return 0; }
例题2【POJ 1236 学校网络】
第一问:缩点后求入度为0的结点数量;
第二问:对于一张有向无环图,可以通过把叶子结点连向根节点使它强连通;
like this:
所以需要连的边数量为max(叶节点,根节点);
叶节点是出度为0的点,根节点是入度为0的点;
注意当只有一个强连通分量时,需要特判,为0;
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<vector> #include<stack> #include<set> using namespace std; const int maxn=110; vector<int> pic[maxn]; int dfn[maxn],low[maxn],ans[maxn]; bool ins[maxn]; stack<int>st; set<int>check[maxn]; int dind=0,block=0; int siz[maxn]; int ans1,in[maxn],out[maxn]; void tarjan(int x) { dind++; dfn[x]=low[x]=dind; ins[x]=true; st.push(x); for (int j=0;j<pic[x].size();j++) { int y=pic[x][j]; if (!dfn[y]){ tarjan(y); low[x]=min(low[x],low[y]); } else if (ins[y]) low[x]=min(low[x],dfn[y]); } if (low[x]==dfn[x]) { block++; siz[block]=0; while (true) { int thi=st.top(); st.pop(); ans[thi]=block; siz[block]++; ins[thi]=false; if (thi==x) break; } } } int main(){ int n,i,j; scanf("%d",&n); for(i=1;i<=n;i++){ scanf("%d",&j); while(j) pic[i].push_back(j),scanf("%d",&j); } for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); for (i=1;i<=n;i++) for (j=0;j<pic[i].size();j++){ int b1=ans[i],b2=ans[pic[i][j]]; if (b1==b2) continue; if (check[b1].count(b2)) continue; check[b1].insert(b2); in[b2]++; out[b1]++; } int root=0,leaf=0; for(int i=1;i<=block;i++){ if(!in[i]) root++; if(!out[i]) leaf++; } printf("%d\n",root); if(block==1) printf("0"); else printf("%d",max(leaf,root)); return 0; }
类似的还有【HDU 2767】
很奇怪的是提交时出现了这样尴尬的问题:
0_0_20950778_21662.cpp
0_0_20950778_21662.cpp(29) : error C3861: “min”: 找不到标识符
0_0_20950778_21662.cpp(31) : error C3861: “min”: 找不到标识符
0_0_20950778_21662.cpp(83) : error C3861: “max”: 找不到标识符
然后在出错的库后面加上了#include "minmax.h",就过了?(莫名其妙)
代码:
#include<iostream> #include "minmax.h" #include<cstdio> #include<cstring> #include<vector> #include<stack> #include<set> using namespace std; const int maxn=20010; vector<int> pic[maxn]; int dfn[maxn],low[maxn],ans[maxn]; bool ins[maxn]; stack<int>st; set<int>check[maxn]; int dind=0,block=0,siz[maxn]; int in[maxn],out[maxn],T; void tarjan(int x) { dind++; dfn[x]=low[x]=dind; ins[x]=true; st.push(x); for (int j=0;j<pic[x].size();j++) { int y=pic[x][j]; if (!dfn[y]){ tarjan(y); low[x]=min(low[x],low[y]); } else if (ins[y]) low[x]=min(low[x],dfn[y]); } if (low[x]==dfn[x]) { block++; siz[block]=0; while (true) { int thi=st.top(); st.pop(); ans[thi]=block; siz[block]++; ins[thi]=false; if (thi==x) break; } } } void init(int n){ for(int i=1;i<=n;i++) pic[i].clear(),check[i].clear();; memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(ans,0,sizeof(ans)); memset(ins,0,sizeof(ins)); while(!st.empty()) st.pop(); dind=0;block=0; memset(siz,0,sizeof(siz)); memset(in,0,sizeof(in)); memset(out,0,sizeof(out)); } int main(){ scanf("%d",&T); while(T--){ int n,i,j,m; scanf("%d%d",&n,&m); init(n); for(i=1;i<=m;i++){ int x,y; scanf("%d%d",&x,&y); pic[x].push_back(y); } for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); for (i=1;i<=n;i++) for (j=0;j<pic[i].size();j++){ int b1=ans[i],b2=ans[pic[i][j]]; if (b1==b2) continue; if (check[b1].count(b2)) continue; check[b1].insert(b2); in[b2]++; out[b1]++; } int root=0,leaf=0; for(int i=1;i<=block;i++){ if(!in[i]) root++; if(!out[i]) leaf++; } if(block==1) printf("0\n"); else printf("%d\n",max(leaf,root)); } return 0; }
————————————————————————————————————————————————————————
来自Paper Cloud的博客,未经允许,请勿转载,谢谢
强连通分量(学习心得)