参考资料:http://www.byvoid.com/blog/scc-tarjan/
几道简单的练习题(hdoj):
强连通:
1269 迷宫城堡 判断是否是一个强连通
2767 Proving Equivalences 至少加几条边让整个图变成强连通
3836 Equivalent Sets 至少加几条边让整个图变成强连通
1827 Summer Holiday 传递的最小费用
3072 Intelligence System 传递的最小费用
3861 The King’s Problem 强连通+二分匹配
3639 Hawk-and-Chicken 强连通缩点 + 树形dp(累加子节点的总权值)
3594 Cactus 仙人掌图
我的代码:
HDOJ1269 迷宫城堡(风格基本参照byvoid资料)
(模板测试题)
#include <cstdio> #include <cstring> #include <vector> #include <iostream> using namespace std; const int NN=10005; int n,m; int Dindex,Stop,Bcnt; bool instack[NN]; int dfn[NN],low[NN]; int Stap[NN]; vector<int> map[NN]; void tarjan(int u) { int v; dfn[u]=low[u]=++Dindex; instack[u]=true; Stap[++Stop]=u; for (int i=0; i<map[u].size(); i++) { v=map[u][i]; if (!dfn[v]) { tarjan(v); low[u]=min(low[u],low[v]); } else if (instack[v]) low[u]=min(low[u],dfn[v]); } if (dfn[u]==low[u]) { Bcnt++; do { v=Stap[Stop--]; instack[v]=false; }while (v!=u); } } void solve() { int i; Stop=Bcnt=Dindex=0; memset(dfn,0,sizeof(dfn)); memset(instack,false,sizeof(instack)); tarjan(1); for (i=2; i<=n; i++) { if (!dfn[i]) { Bcnt=2; return ; } } } int main() { int i,a,b; while (scanf("%d%d",&n,&m)==2) { if (n==0 && m==0) break; for (int i=1; i<=n; i++) map[i].clear(); for (int i=0; i<m; i++) { scanf("%d%d",&a,&b); map[a].push_back(b); } solve(); if (Bcnt>1) printf("No\n"); else printf("Yes\n"); } return 0; }
HDOJ2767(HDOJ3836题目是一样的,改改输入方式就过了):
(先求连通块,若图已强连通,不用添边;若连通块之间完全不连通则要添加bcnt(连通块数量)条边;若有x1个连通块有入边,则添bcnt-x1条边即可;若有x2个连通块有出边,则添bcnt-x2条边即可。第一种情况特判,后三种情况即是无入边连通块数a与无出边连通块数b的最大值)
#include <cstdio> #include <cstring> #include <vector> #include <iostream> using namespace std; const int NN=20005; const int MM=50010; vector<int> map[NN]; int n,m,bcnt,top,index; int dfn[NN],low[NN],stack[NN],belong[NN],chu[NN],ru[NN]; int from[MM],to[MM]; bool instack[NN]; void init() { bcnt=0; top=0; index=0; memset(instack,false,sizeof(instack)); memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); } void tarjan(int u) { dfn[u]=low[u]=++index; instack[u]=true; stack[top++]=u; int i,j,v; for (i=0; i<map[u].size(); i++) { v=map[u][i]; if (!dfn[v]) { tarjan(v); if (low[v]<low[u]) low[u]=low[v]; } else if (instack[v] && dfn[v]<low[u]) low[u]=dfn[v]; } if (low[u]==dfn[u]) { bcnt++; do { j=stack[--top]; instack[j]=false; belong[j]=bcnt; }while (u!=j); } } void solve() { for (int i=1; i<=n; i++) if (!dfn[i]) tarjan(i); if (bcnt==1) { printf("0\n"); return ; } for (int i=1; i<=bcnt; i++) ru[i]=chu[i]=0; for (int i=1; i<=m; i++) { int x=belong[from[i]]; int y=belong[to[i]]; if (x!=y) { ru[y]++; chu[x]++; } } int a,b; a=b=0; for (int i=1; i<=bcnt; i++) { if (ru[i]==0) a++; if (chu[i]==0) b++; } if (a>b) b=a; printf("%d\n",b); } int main() { int cas; scanf("%d",&cas); while (cas--) { scanf("%d%d",&n,&m); init(); for (int i=1; i<=n; i++) map[i].clear(); for (int i=1; i<=m; i++) { int a,b; scanf("%d%d",&a,&b); map[a].push_back(b); from[i]=a; to[i]=b; } solve(); } return 0; }
HDOJ1827:
(求出所有无入边的连通块的最小权,这些最小权的和即答案)
#include <cstdio> #include <cstring> #include <vector> #include <iostream> using namespace std; const int NN=1002; const int MM=2004; const int INF=0x7fffffff; vector<int> map[NN]; int n,m,Dindex,top,Bcnt; int dfn[NN],w[NN],low[NN],belong[NN],stack[NN],cost[NN]; bool instack[NN],ru[NN]; void tarjan(int u) { dfn[u]=low[u]=++Dindex; stack[++top]=u; instack[u]=true; int v; for (int i=0; i<map[u].size(); i++) { v=map[u][i]; if (!dfn[v]) { tarjan(v); low[u]=min(low[u],low[v]); } else if (instack[v]) low[u]=min(low[u],dfn[v]); } if (low[u]==dfn[u]) { Bcnt++; do { v=stack[top--]; belong[v]=Bcnt; instack[v]=false; }while (v!=u); } } void solve() { int u,v,i; memset(dfn,0,sizeof(dfn)); memset(instack,false,sizeof(instack)); Dindex=top=Bcnt=0; for (i=1; i<=n; i++) if (!dfn[i]) tarjan(i); memset(ru,false,sizeof(ru)); for (u=1; u<=n; u++) { for (i=0; i<map[u].size(); i++) { v=map[u][i]; if (belong[u]!=belong[v]) ru[belong[v]]=true; } } for (i=1; i<=Bcnt; i++) cost[i]=INF; for (i=1; i<=n; i++) { if (!ru[belong[i]]) cost[belong[i]]=min(cost[belong[i]],w[i]); } int ans1=0; int ans2=0; for (i=1; i<=Bcnt; i++) { if (!ru[i]) { ans1++; ans2+=cost[i]; } } printf("%d %d\n",ans1,ans2); } int main() { while (scanf("%d%d",&n,&m)==2) { for (int i=1; i<=n; i++) { scanf("%d",&w[i]); map[i].clear(); } int a,b; for (int i=1; i<=m; i++) { scanf("%d%d",&a,&b); map[a].push_back(b); } solve(); } return 0; }
HDOJ3072(跟上题很类似,想起一些天做比赛时看到这题,居然傻了,数据范围又大~):
(求各强连通块最小入度和,注意有结点0所在连通块的最小入度为0)
#include <cstdio> #include <vector> #include <queue> #include <cstring> #include <iostream> using namespace std; const int NN=50005; const int MM=100010; const int INF=0x7fffffff; struct Edge{ int u,v,w,next; }edge[MM]; int n,m,Ecnt,Bcnt,Dindex,top; int st[NN],dfn[NN],low[NN],belong[NN],stack[NN],cost[NN]; bool instack[NN]; void addedge(int u,int v,int w) { Ecnt++; edge[Ecnt].u=u; edge[Ecnt].v=v; edge[Ecnt].w=w; edge[Ecnt].next=st[u]; st[u]=Ecnt; } void init() { Ecnt=0; Dindex=top=Bcnt=0; int i; for (i=0; i<n; i++) { st[i]=dfn[i]=0; instack[i]=false; } } void tarjan(int u) { dfn[u]=low[u]=++Dindex; stack[++top]=u; instack[u]=true; int i,v; for (i=st[u]; i; i=edge[i].next) { v=edge[i].v; if (!dfn[v]) { tarjan(v); low[u]=min(low[u],low[v]); } else if (instack[v]) low[u]=min(low[u],dfn[v]); } if (low[u]==dfn[u]) { Bcnt++; do { v=stack[top--]; instack[v]=false; belong[v]=Bcnt; }while (v!=u); } } void solve() { int i,k1,k2; for (i=0; i<n; i++) if (!dfn[i]) tarjan(i); for (i=1; i<=Bcnt; i++) cost[i]=INF; cost[belong[0]]=0; for (i=1; i<=Ecnt; i++) { k1=belong[edge[i].u]; k2=belong[edge[i].v]; if (k1!=k2) cost[k2]=min(cost[k2],edge[i].w); } int ans=0; for (i=1; i<=Bcnt; i++) ans+=cost[i]; printf("%d\n",ans); } int main() { int i,a,b,c; while (scanf("%d%d",&n,&m)==2) { init(); for (i=0; i<m; i++) { scanf("%d%d%d",&a,&b,&c); addedge(a,b,c); } solve(); } return 0; }
图论啊,图论~
共勉。