专题训练之强连通分量
tarjan模板
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int maxn=20010; 6 const int maxm=50010; 7 struct edge{ 8 int to,nxt; 9 }edge[maxm]; 10 int head[maxn],tot; 11 int low[maxn],dfn[maxn],stack[maxn],belong[maxn]; 12 int index,top; 13 int scc; 14 bool vis[maxn]; 15 int num[maxn]; 16 17 void addedge(int u,int v) 18 { 19 edge[tot].to=v; 20 edge[tot].nxt=head[u]; 21 head[u]=tot++; 22 } 23 24 void tarjan(int u) 25 { 26 int v; 27 low[u]=dfn[u]=++index; 28 stack[top++]=u; 29 vis[u]=true; 30 for ( int i=head[u];i!=-1;i=edge[i].nxt ) { 31 v=edge[i].to; 32 if ( !dfn[v] ) { 33 tarjan(v); 34 low[u]=min(low[u],low[v]); 35 } 36 else if ( vis[v] ) low[u]=min(low[u],dfn[v]); 37 } 38 if ( low[u]==dfn[u] ) { 39 scc++; 40 do { 41 v=stack[--top]; 42 vis[v]=false; 43 belong[v]=scc; 44 num[scc]++; 45 } 46 while ( v!=u ); 47 } 48 } 49 50 void solve(int N) 51 { 52 memset(dfn,0,sizeof(dfn)); 53 memset(vis,false,sizeof(vis)); 54 memset(num,0,sizeof(num)); 55 index=scc=top=0; 56 for ( int i=1;i<=N;i++ ) { 57 if ( !dfn[i] ) tarjan(i); 58 } 59 } 60 61 void init() 62 { 63 tot=0; 64 memset(head,-1,sizeof(head)); 65 }
1.(HDOJ1269)http://acm.hdu.edu.cn/showproblem.php?pid=1269
分析:裸题,判断scc是否为1即可
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int maxn=1e4+10; 6 const int maxm=1e5+10; 7 struct edge{ 8 int to,nxt; 9 }edge[maxm]; 10 int head[maxn],tot; 11 int low[maxn],dfn[maxn],stack[maxn],belong[maxn]; 12 int index,top; 13 int scc,n; 14 bool vis[maxn]; 15 int num[maxn]; 16 17 void addedge(int u,int v) 18 { 19 edge[tot].to=v; 20 edge[tot].nxt=head[u]; 21 head[u]=tot++; 22 } 23 24 void tarjan(int u) 25 { 26 int v; 27 low[u]=dfn[u]=++index; 28 stack[top++]=u; 29 vis[u]=true; 30 for ( int i=head[u];i!=-1;i=edge[i].nxt ) { 31 v=edge[i].to; 32 if ( !dfn[v] ) { 33 tarjan(v); 34 low[u]=min(low[u],low[v]); 35 } 36 else if ( vis[v] ) low[u]=min(low[u],dfn[v]); 37 } 38 if ( low[u]==dfn[u] ) { 39 scc++; 40 do { 41 v=stack[--top]; 42 vis[v]=false; 43 belong[v]=scc; 44 num[scc]++; 45 } 46 while ( v!=u ); 47 } 48 } 49 50 void solve() 51 { 52 memset(dfn,0,sizeof(dfn)); 53 memset(vis,false,sizeof(vis)); 54 memset(num,0,sizeof(num)); 55 index=scc=top=0; 56 for ( int i=1;i<=n;i++ ) { 57 if ( !dfn[i] ) tarjan(i); 58 } 59 } 60 61 void init() 62 { 63 tot=0; 64 memset(head,-1,sizeof(head)); 65 } 66 67 int main() 68 { 69 int m,i,j,k,x,y; 70 while ( scanf("%d%d",&n,&m)!=EOF && (n+m) ) { 71 init(); 72 for ( i=0;i<m;i++ ) { 73 scanf("%d%d",&x,&y); 74 addedge(x,y); 75 } 76 solve(); 77 if ( scc==1 ) printf("Yes\n"); 78 else printf("No\n"); 79 } 80 return 0; 81 }
2.(HDOJ1827)http://acm.hdu.edu.cn/showproblem.php?pid=1827
分析:利用tarjan进行缩点得到新的GAD图,然后再根据新图中每个节点的入度和出度进行相应的操作。因为该题需要花费更少,所有只需要求那些所有入度为0的点所需要花费的费用。而对于新图中每个点的花费,可以很方便的在tarjan中进行更新
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int maxn=1005; 6 const int maxm=2005; 7 const int inf=1e9; 8 struct edge{ 9 int to,nxt; 10 }edge[maxm]; 11 int head[maxn],tot; 12 int low[maxn],dfn[maxn],stack[maxn],belong[maxn]; 13 int index,top; 14 int scc,n; 15 bool vis[maxn]; 16 int num[maxn]; 17 int cost[maxn],in[maxn],out[maxn],cost_[maxn]; 18 19 void addedge(int u,int v) 20 { 21 edge[tot].to=v; 22 edge[tot].nxt=head[u]; 23 head[u]=tot++; 24 } 25 26 void tarjan(int u) 27 { 28 int v; 29 low[u]=dfn[u]=++index; 30 stack[top++]=u; 31 vis[u]=true; 32 for ( int i=head[u];i!=-1;i=edge[i].nxt ) { 33 v=edge[i].to; 34 if ( !dfn[v] ) { 35 tarjan(v); 36 low[u]=min(low[u],low[v]); 37 } 38 else if ( vis[v] ) low[u]=min(low[u],dfn[v]); 39 } 40 if ( low[u]==dfn[u] ) { 41 scc++; 42 do { 43 v=stack[--top]; 44 vis[v]=false; 45 belong[v]=scc; 46 num[scc]++; 47 cost_[scc]=min(cost_[scc],cost[v]); 48 } 49 while ( v!=u ); 50 } 51 } 52 53 void solve() 54 { 55 memset(dfn,0,sizeof(dfn)); 56 memset(vis,false,sizeof(vis)); 57 memset(num,0,sizeof(num)); 58 for ( int i=1;i<=n;i++ ) cost_[i]=inf; 59 index=scc=top=0; 60 for ( int i=1;i<=n;i++ ) { 61 if ( !dfn[i] ) tarjan(i); 62 } 63 } 64 65 void init() 66 { 67 tot=0; 68 memset(head,-1,sizeof(head)); 69 } 70 71 int main() 72 { 73 int m,i,j,k,x,y,ans,cnt,v; 74 while ( scanf("%d%d",&n,&m)!=EOF ) { 75 init(); 76 for ( i=1;i<=n;i++ ) scanf("%d",&cost[i]); 77 for ( i=1;i<=m;i++ ) { 78 scanf("%d%d",&x,&y); 79 addedge(x,y); 80 } 81 solve(); 82 memset(in,0,sizeof(in)); 83 memset(out,0,sizeof(out)); 84 for ( i=1;i<=n;i++ ) { 85 for ( j=head[i];j!=-1;j=edge[j].nxt ) { 86 v=edge[j].to; 87 if ( belong[i]!=belong[v] ) { 88 in[belong[v]]++; 89 out[belong[i]]++; 90 } 91 } 92 } 93 ans=0; 94 cnt=0; 95 for ( i=1;i<=scc;i++ ) { 96 if ( !in[i] ) { 97 ans+=cost_[i]; 98 cnt++; 99 } 100 } 101 printf("%d %d\n",cnt,ans); 102 } 103 return 0; 104 }
3.(HDOJ2767、HDOJ3836)
http://acm.hdu.edu.cn/showproblem.php?pid=2767
http://acm.hdu.edu.cn/showproblem.php?pid=3836
题意:添加多少条可以使得整个图为一个强连通分量。先求出强连通分量的个数,然后需要添加的边为出度和入度为0中较大的那个值(即没有点的入度和出度为0)。需要特别注意的是本身只有一个强连通分量则直接输出0
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int maxn=20050; 6 const int maxm=50010; 7 struct edge{ 8 int to,nxt; 9 }edge[maxm]; 10 int head[maxn],tot; 11 int low[maxn],dfn[maxn],stack[maxn],belong[maxn]; 12 int index,top; 13 int scc; 14 bool vis[maxn]; 15 int num[maxn],in[maxn],out[maxn]; 16 17 void addedge(int u,int v) 18 { 19 edge[tot].to=v; 20 edge[tot].nxt=head[u]; 21 head[u]=tot++; 22 } 23 24 void tarjan(int u) 25 { 26 int v; 27 low[u]=dfn[u]=++index; 28 stack[top++]=u; 29 vis[u]=true; 30 for ( int i=head[u];i!=-1;i=edge[i].nxt ) { 31 v=edge[i].to; 32 if ( !dfn[v] ) { 33 tarjan(v); 34 low[u]=min(low[u],low[v]); 35 } 36 else if ( vis[v] ) low[u]=min(low[u],dfn[v]); 37 } 38 if ( low[u]==dfn[u] ) { 39 scc++; 40 do { 41 v=stack[--top]; 42 vis[v]=false; 43 belong[v]=scc; 44 num[scc]++; 45 } 46 while ( v!=u ); 47 } 48 } 49 50 void solve(int N) 51 { 52 memset(dfn,0,sizeof(dfn)); 53 memset(vis,false,sizeof(vis)); 54 memset(num,0,sizeof(num)); 55 index=scc=top=0; 56 for ( int i=1;i<=N;i++ ) { 57 if ( !dfn[i] ) tarjan(i); 58 } 59 } 60 61 void init() 62 { 63 tot=0; 64 memset(head,-1,sizeof(head)); 65 } 66 67 int main() 68 { 69 int T,i,j,k,n,m,x,y,z,ans,cnt1,cnt2; 70 scanf("%d",&T); 71 while ( T-- ) { 72 init(); 73 scanf("%d%d",&n,&m); 74 while ( m-- ) { 75 scanf("%d%d",&x,&y); 76 addedge(x,y); 77 } 78 solve(n); 79 memset(in,0,sizeof(in)); 80 memset(out,0,sizeof(out)); 81 for ( i=1;i<=n;i++ ) { 82 for ( j=head[i];j!=-1;j=edge[j].nxt ) { 83 int v=edge[j].to; 84 if ( belong[v]!=belong[i] ) { 85 in[belong[v]]++; 86 out[belong[i]]++; 87 } 88 } 89 } 90 cnt1=cnt2=0; 91 for ( i=1;i<=scc;i++ ) { 92 if ( in[i]==0 ) cnt1++; 93 if ( out[i]==0 ) cnt2++; 94 } 95 ans=max(cnt1,cnt2); 96 if ( scc==1 ) ans=0; 97 printf("%d\n",ans); 98 } 99 return 0; 100 }
4.(HDOJ3639)http://acm.hdu.edu.cn/showproblem.php?pid=3639
题意:有n个小孩要相互投票,有m条两人之间的单向的支持关系,求获得支持最多的人的支持数,并要求把这些人的编号(0~n-1)按升序排列输出来。
分析:首先利用tarjan进行缩点构建新图,首先明确得票数最高的人一定在出度为0的强连通分量团体中,所以当前任务就是确定对于每个出度为0的强连通分量团体有多少人能够直接或间接指向它们。这时候就要在新图上构建反向边,对于出度为0的点i进行搜索,经过的所有点的人数总和(每个点都代表一部分人数)就是支持这个团体i中每个人的总得票数。记录每个出度为0的点的得票数,取最大值。最后对于新图中的每个点当其的得票数为最大值时,其强连通分量团体中的所有点都能够当选。
注意:从样例中可以得到一个人可以同时支持多个人,且支持的人都能得到他的全部票数
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #include<queue> 6 #include<set> 7 using namespace std; 8 const int maxn=20010; 9 const int maxm=50010; 10 struct edge{ 11 int to,nxt; 12 }edge[maxm]; 13 int head[maxn],tot; 14 int low[maxn],dfn[maxn],stack[maxn],belong[maxn],cnt[maxn]; 15 int index,top; 16 int scc; 17 bool vis[maxn],vis_[maxn]; 18 int num[maxn],in[maxn],out[maxn]; 19 vector<int>G[maxn]; 20 21 void addedge(int u,int v) 22 { 23 edge[tot].to=v; 24 edge[tot].nxt=head[u]; 25 head[u]=tot++; 26 } 27 28 void addedge_(int u,int v) 29 { 30 G[u].push_back(v); 31 } 32 33 void tarjan(int u) 34 { 35 int v; 36 low[u]=dfn[u]=++index; 37 stack[top++]=u; 38 vis[u]=true; 39 for ( int i=head[u];i!=-1;i=edge[i].nxt ) { 40 v=edge[i].to; 41 if ( !dfn[v] ) { 42 tarjan(v); 43 low[u]=min(low[u],low[v]); 44 } 45 else if ( vis[v] ) low[u]=min(low[u],dfn[v]); 46 } 47 if ( low[u]==dfn[u] ) { 48 scc++; 49 do { 50 v=stack[--top]; 51 vis[v]=false; 52 belong[v]=scc; 53 num[scc]++; 54 } 55 while ( v!=u ); 56 } 57 } 58 59 void solve(int N) 60 { 61 memset(dfn,0,sizeof(dfn)); 62 memset(vis,false,sizeof(vis)); 63 memset(num,0,sizeof(num)); 64 index=scc=top=0; 65 for ( int i=1;i<=N;i++ ) { 66 if ( !dfn[i] ) tarjan(i); 67 } 68 } 69 70 void init() 71 { 72 tot=0; 73 memset(head,-1,sizeof(head)); 74 } 75 76 int BFS(int u) 77 { 78 memset(vis,false,sizeof(vis)); 79 queue<int>que; 80 que.push(u); 81 vis[u]=true; 82 int now=num[u]; 83 while ( !que.empty() ) { 84 int v=que.front(); 85 que.pop(); 86 for ( int i=0;i<G[v].size();i++ ) { 87 int j=G[v][i]; 88 if ( !vis[j] ) { 89 vis[j]=true; 90 que.push(j); 91 now+=num[j]; 92 } 93 } 94 } 95 return now; 96 } 97 98 int main() 99 { 100 int T,i,j,k,h,x,y,z,n,m,ans,now,cnt_; 101 scanf("%d",&T); 102 for ( h=1;h<=T;h++ ) { 103 scanf("%d%d",&n,&m); 104 init(); 105 for ( i=1;i<=n;i++ ) G[i].clear(); 106 for ( i=1;i<=m;i++ ) { 107 scanf("%d%d",&x,&y); 108 x++;y++; 109 addedge(x,y); 110 } 111 solve(n); 112 memset(in,0,sizeof(in)); 113 memset(out,0,sizeof(out)); 114 memset(cnt,0,sizeof(cnt)); 115 memset(vis_,false,sizeof(vis_)); 116 for ( i=1;i<=n;i++ ) { 117 for ( j=head[i];j!=-1;j=edge[j].nxt ) { 118 int v=edge[j].to; 119 if ( belong[v]!=belong[i] ) { 120 in[belong[v]]++; 121 out[belong[i]]++; 122 addedge_(belong[v],belong[i]); 123 } 124 } 125 } 126 ans=0; 127 k=0; 128 for ( i=1;i<=scc;i++ ) { 129 if ( out[i]==0 ) { 130 cnt[i]=BFS(i); 131 if ( cnt[i]>ans ) ans=cnt[i]; 132 } 133 } 134 for ( i=1;i<=scc;i++ ) { 135 if ( cnt[i]==ans ) { 136 vis_[i]=true; 137 k+=num[i]; 138 } 139 } 140 printf("Case %d: %d\n",h,ans-1); 141 cnt_=0; 142 for ( i=1;i<=n;i++ ) { 143 if ( vis_[belong[i]] ) { 144 printf("%d",i-1); 145 if ( ++cnt_==k ) { 146 printf("\n"); 147 break; 148 } 149 else printf(" "); 150 } 151 } 152 } 153 return 0; 154 }
5.(HDOJ3072)http://acm.hdu.edu.cn/showproblem.php?pid=3072
题意:有n个人编号从0到n-1,给出m组关系<u,v,w>表示u联系v需要费用w(但不代表v联系u需要费用w)。若一个集合中 任意两个人可以互相联系(不管是直接联系的还是通过其他人间接联系的),那么在这个集合里面联系的费用可以忽略。现在你是编号0,问你联系到所有人的最小费用。题目保证至少有一组方案使得你可以联系到所有人。
分析:tarjan+贪心。利用tarjan进行缩点后得到新图,然后需要求出新图的带权最小生成树。因为每个点必定会到达一次,所有对于每个点考虑边权最小的一条入边即可(因为最后这个图必定是联通的)。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=50010; const int maxm=100010; const int inf=1e9; struct edge{ int to,nxt,val; }edge[maxm]; int head[maxn],tot; int low[maxn],dfn[maxn],stack[maxn],belong[maxn]; int index,top; int scc; bool vis[maxn]; int num[maxn],dis[maxn]; void addedge(int u,int v,int val) { edge[tot].to=v; edge[tot].nxt=head[u]; edge[tot].val=val; head[u]=tot++; } void tarjan(int u) { int v; low[u]=dfn[u]=++index; stack[top++]=u; vis[u]=true; for ( int i=head[u];i!=-1;i=edge[i].nxt ) { v=edge[i].to; if ( !dfn[v] ) { tarjan(v); low[u]=min(low[u],low[v]); } else if ( vis[v] ) low[u]=min(low[u],dfn[v]); } if ( low[u]==dfn[u] ) { scc++; do { v=stack[--top]; vis[v]=false; belong[v]=scc; num[scc]++; } while ( v!=u ); } } void solve(int N) { memset(dfn,0,sizeof(dfn)); memset(vis,false,sizeof(vis)); memset(num,0,sizeof(num)); index=scc=top=0; for ( int i=1;i<=N;i++ ) { if ( !dfn[i] ) tarjan(i); } } void init() { tot=0; memset(head,-1,sizeof(head)); } int main() { int n,m,i,j,k,x,y,z,ans; while ( scanf("%d%d",&n,&m)!=EOF ) { init(); while ( m-- ) { scanf("%d%d%d",&x,&y,&z); x++;y++; addedge(x,y,z); } solve(n); for ( i=1;i<=scc;i++ ) dis[i]=inf; for ( i=1;i<=n;i++ ) { for ( j=head[i];j!=-1;j=edge[j].nxt ) { int v=edge[j].to; x=belong[i]; y=belong[v]; if ( x!=y ) dis[y]=min(dis[y],edge[j].val); } } ans=0; dis[belong[1]]=0; for ( i=1;i<=scc;i++ ) ans+=dis[i]; printf("%d\n",ans); } return 0; }
6.(HDOJ3861)http://acm.hdu.edu.cn/showproblem.php?pid=3861
题意:有一个国王,有n个城市和m条路径。先将国王分成几个地区,地区满足:若城市u能到城市v同时城市v能到城市u,则它们必定属于一个地区。于此同时还有其他点也可以属于该地区,即一个地区的两点u,v一定存在一条不经过其他地区的从u到v或者从v到u的线路。
分析:tarjan+最小路径覆盖。先用tarjan进行缩点,在新图的基础上构建二分图匹配求出最大匹配进而得到最小路径数。注意点数较多,所有二分匹配用邻接表而不是临界矩阵存。同时点数好像比所给的范围要大的多
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 using namespace std; 6 const int maxn=20010; 7 const int maxm=100010; 8 struct edge{ 9 int to,nxt,val; 10 }edge[maxm]; 11 int head[maxn],tot; 12 int low[maxn],dfn[maxn],stack[maxn],belong[maxn],girl[maxn]; 13 int index,top; 14 int scc,n; 15 bool vis[maxn]; 16 vector<int>G[maxn]; 17 18 void addedge(int u,int v) 19 { 20 edge[tot].to=v; 21 edge[tot].nxt=head[u]; 22 head[u]=tot++; 23 } 24 25 void tarjan(int u) 26 { 27 int v; 28 low[u]=dfn[u]=++index; 29 stack[top++]=u; 30 vis[u]=true; 31 for ( int i=head[u];i!=-1;i=edge[i].nxt ) { 32 v=edge[i].to; 33 if ( !dfn[v] ) { 34 tarjan(v); 35 low[u]=min(low[u],low[v]); 36 } 37 else if ( vis[v] ) low[u]=min(low[u],dfn[v]); 38 } 39 if ( low[u]==dfn[u] ) { 40 scc++; 41 do { 42 v=stack[--top]; 43 vis[v]=false; 44 belong[v]=scc; 45 } 46 while ( v!=u ); 47 } 48 } 49 50 void solve() 51 { 52 memset(dfn,0,sizeof(dfn)); 53 memset(vis,false,sizeof(vis)); 54 index=scc=top=0; 55 for ( int i=1;i<=n;i++ ) { 56 if ( !dfn[i] ) tarjan(i); 57 } 58 } 59 60 void init() 61 { 62 tot=0; 63 memset(head,-1,sizeof(head)); 64 } 65 66 bool find(int x) 67 { 68 int i,j; 69 for ( i=0;i<G[x].size();i++ ) { 70 j=G[x][i]; 71 if ( vis[j]==false ) { 72 vis[j]=true; 73 if ( girl[j]==0 || find(girl[j]) ) { 74 girl[j]=x; 75 return true; 76 } 77 } 78 } 79 return false; 80 } 81 82 int main() 83 { 84 int m,i,j,k,x,y,z,ans,T; 85 scanf("%d",&T); 86 while ( T-- ) { 87 scanf("%d%d",&n,&m); 88 init(); 89 while ( m-- ) { 90 scanf("%d%d",&x,&y); 91 addedge(x,y); 92 } 93 solve(); 94 for ( i=1;i<=scc;i++ ) G[i].clear(); 95 for ( i=1;i<=n;i++ ) { 96 for ( j=head[i];j!=-1;j=edge[j].nxt ) { 97 int v=edge[j].to; 98 x=belong[i]; 99 y=belong[v]; 100 if ( x!=y ) G[x].push_back(y); 101 } 102 } 103 ans=0; 104 memset(girl,0,sizeof(girl)); 105 for ( i=1;i<=scc;i++ ) { 106 memset(vis,false,sizeof(vis)); 107 if ( find(i) ) ans++; 108 } 109 ans=scc-ans; 110 printf("%d\n",ans); 111 } 112 return 0; 113 }
7.(POJ1904)http://poj.org/problem?id=1904
题意:一个国王有n个王子,同时有n个女孩。每个王子都有自己喜欢的若干个女孩,现给定一个合法的完备匹配(也就是一个王子娶其中一个自己喜欢女孩),求每个王子可以选择哪些女孩可以让剩下的每个王子依旧能够选择到自己喜欢的一个女孩。
分析:给王子标号1-n,女孩标号n+1-2*n,王子喜欢女孩则连一条从王子到女孩的边,给定的合法匹配中连一条女孩到王子的边。对于某个王子看与他连边的女孩中是否和他属于同一个强连通分量。
推荐一个较为详细的分析http://www.cnblogs.com/zxndgv/archive/2011/08/06/2129333.html
注意:n,m的范围看清楚,不然很容易RE。最终的答案一定是按升序输出的。在一个强连通分量中可能存在王子不喜欢某个女孩的情况,这时候我们采用的方法是对于每个点(只考虑代表王子的点),遍历他全部的边看边的两端是否属于同一个强连通分量(因为刚开始建的边一定满足男孩喜欢女孩),属于就保存下来。后排序输出
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 using namespace std; 6 const int maxn=5010; 7 const int maxm=250010; 8 struct edge{ 9 int to,nxt; 10 }edge[maxm]; 11 int head[maxn],tot; 12 int low[maxn],dfn[maxn],stack[maxn],belong[maxn],ans[maxn]; 13 int index,top; 14 int scc,n; 15 bool vis[maxn]; 16 int num[maxn]; 17 18 void addedge(int u,int v) 19 { 20 edge[tot].to=v; 21 edge[tot].nxt=head[u]; 22 head[u]=tot++; 23 } 24 25 void tarjan(int u) 26 { 27 int v; 28 low[u]=dfn[u]=++index; 29 stack[top++]=u; 30 vis[u]=true; 31 for ( int i=head[u];i!=-1;i=edge[i].nxt ) { 32 v=edge[i].to; 33 if ( !dfn[v] ) { 34 tarjan(v); 35 low[u]=min(low[u],low[v]); 36 } 37 else if ( vis[v] ) low[u]=min(low[u],dfn[v]); 38 } 39 if ( low[u]==dfn[u] ) { 40 scc++; 41 do { 42 v=stack[--top]; 43 vis[v]=false; 44 belong[v]=scc; 45 num[scc]++; 46 } 47 while ( v!=u ); 48 } 49 } 50 51 void solve() 52 { 53 memset(dfn,0,sizeof(dfn)); 54 memset(vis,false,sizeof(vis)); 55 memset(num,0,sizeof(num)); 56 index=scc=top=0; 57 for ( int i=1;i<=2*n;i++ ) { 58 if ( !dfn[i] ) tarjan(i); 59 } 60 for ( int i=1;i<=n;i++ ) { 61 int cnt=0; 62 for ( int j=head[i];j!=-1;j=edge[j].nxt ) { 63 int v=edge[j].to; 64 int x=belong[i]; 65 int y=belong[v]; 66 if ( x==y ) ans[cnt++]=v-n; 67 } 68 sort(ans,ans+cnt); 69 printf("%d",cnt); 70 for ( int k=0;k<cnt;k++ ) printf(" %d",ans[k]); 71 printf("\n"); 72 } 73 } 74 75 void init() 76 { 77 tot=0; 78 memset(head,-1,sizeof(head)); 79 } 80 81 int main() 82 { 83 int m,i,j,k,x,y,z; 84 while ( scanf("%d",&n)!=EOF ) { 85 init(); 86 for ( i=1;i<=n;i++ ) { 87 scanf("%d",&m); 88 for ( j=1;j<=m;j++ ) { 89 scanf("%d",&x); 90 addedge(i,x+n); 91 } 92 } 93 for ( i=1;i<=n;i++ ) { 94 scanf("%d",&x); 95 addedge(x+n,i); 96 } 97 solve(); 98 } 99 return 0; 100 }
8.(POJ2186)http://poj.org/problem?id=2186
题意:有n头牛,m条关系(A,B),即A认为B很受欢迎。先求是有多少人受到其他所有人的欢迎
分析:利用tarjan进行缩点,对于最受欢迎的人必定是出度为0的点,然后在利用反向图+bfs判断该点是否能到达其他所有点
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #include<queue> 6 using namespace std; 7 const int maxn=10010; 8 const int maxm=50010; 9 struct edge{ 10 int to,nxt; 11 }edge[maxm]; 12 int head[maxn],tot; 13 int low[maxn],dfn[maxn],stack[maxn],belong[maxn]; 14 int index,top; 15 int scc; 16 bool vis[maxn]; 17 int num[maxn],in[maxn],out[maxn]; 18 vector<int>G[maxn]; 19 20 void addedge(int u,int v) 21 { 22 edge[tot].to=v; 23 edge[tot].nxt=head[u]; 24 head[u]=tot++; 25 } 26 27 void tarjan(int u) 28 { 29 int v; 30 low[u]=dfn[u]=++index; 31 stack[top++]=u; 32 vis[u]=true; 33 for ( int i=head[u];i!=-1;i=edge[i].nxt ) { 34 v=edge[i].to; 35 if ( !dfn[v] ) { 36 tarjan(v); 37 low[u]=min(low[u],low[v]); 38 } 39 else if ( vis[v] ) low[u]=min(low[u],dfn[v]); 40 } 41 if ( low[u]==dfn[u] ) { 42 scc++; 43 do { 44 v=stack[--top]; 45 vis[v]=false; 46 belong[v]=scc; 47 num[scc]++; 48 } 49 while ( v!=u ); 50 } 51 } 52 53 void solve(int N) 54 { 55 memset(dfn,0,sizeof(dfn)); 56 memset(vis,false,sizeof(vis)); 57 memset(num,0,sizeof(num)); 58 index=scc=top=0; 59 for ( int i=1;i<=N;i++ ) { 60 if ( !dfn[i] ) tarjan(i); 61 } 62 } 63 64 void init() 65 { 66 tot=0; 67 memset(head,-1,sizeof(head)); 68 } 69 70 bool BFS(int x) 71 { 72 int i,j,k,u,v; 73 memset(vis,false,sizeof(vis)); 74 vis[x]=true; 75 queue<int>que; 76 que.push(x); 77 while ( !que.empty() ) { 78 u=que.front(); 79 que.pop(); 80 for ( i=0;i<G[u].size();i++ ) { 81 v=G[u][i]; 82 if ( !vis[v] ) { 83 vis[v]=true; 84 que.push(v); 85 } 86 } 87 } 88 for ( i=1;i<=scc;i++ ) { 89 if ( !vis[i] ) return false; 90 } 91 return true; 92 } 93 94 int main() 95 { 96 int n,m,i,j,k,x,y,z,ans,now; 97 bool flag; 98 while ( scanf("%d%d",&n,&m)!=EOF ) { 99 init(); 100 while ( m-- ) { 101 scanf("%d%d",&x,&y); 102 addedge(x,y); 103 } 104 solve(n); 105 memset(in,0,sizeof(in)); 106 memset(out,0,sizeof(out)); 107 for ( i=1;i<=n;i++ ) G[i].clear(); 108 for ( i=1;i<=n;i++ ) { 109 for ( j=head[i];j!=-1;j=edge[j].nxt ) { 110 int v=edge[j].to; 111 x=belong[i]; 112 y=belong[v]; 113 if ( x!=y ) { 114 out[x]++; 115 in[y]++; 116 G[y].push_back(x); 117 } 118 } 119 } 120 now=0; 121 for ( i=1;i<=scc;i++ ) { 122 if ( out[i]==0 ) { 123 now++; 124 k=i; 125 } 126 } 127 if ( now!=1 ) { 128 printf("0\n"); 129 continue; 130 } 131 flag=BFS(k); 132 if ( flag=false ) printf("0\n"); 133 else printf("%d\n",num[k]); 134 } 135 return 0; 136 } 137
10.(POJ2553)http://poj.org/problem?id=2553
题意:给定一个有向图,若v为源点,那么如果v能够到达的点u,则u也一定可以到达点v。
分析:满足条件的源点一定是处于出度为0的强连通分量中
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int maxn=20010; 6 const int maxm=50010; 7 struct edge{ 8 int to,nxt; 9 }edge[maxm]; 10 int head[maxn],tot; 11 int low[maxn],dfn[maxn],stack[maxn],belong[maxn]; 12 int index,top; 13 int scc; 14 bool vis[maxn]; 15 int num[maxn],out[maxn]; 16 17 void addedge(int u,int v) 18 { 19 edge[tot].to=v; 20 edge[tot].nxt=head[u]; 21 head[u]=tot++; 22 } 23 24 void tarjan(int u) 25 { 26 int v; 27 low[u]=dfn[u]=++index; 28 stack[top++]=u; 29 vis[u]=true; 30 for ( int i=head[u];i!=-1;i=edge[i].nxt ) { 31 v=edge[i].to; 32 if ( !dfn[v] ) { 33 tarjan(v); 34 low[u]=min(low[u],low[v]); 35 } 36 else if ( vis[v] ) low[u]=min(low[u],dfn[v]); 37 } 38 if ( low[u]==dfn[u] ) { 39 scc++; 40 do { 41 v=stack[--top]; 42 vis[v]=false; 43 belong[v]=scc; 44 num[scc]++; 45 } 46 while ( v!=u ); 47 } 48 } 49 50 void solve(int N) 51 { 52 memset(dfn,0,sizeof(dfn)); 53 memset(vis,false,sizeof(vis)); 54 memset(num,0,sizeof(num)); 55 index=scc=top=0; 56 for ( int i=1;i<=N;i++ ) { 57 if ( !dfn[i] ) tarjan(i); 58 } 59 } 60 61 void init() 62 { 63 tot=0; 64 memset(head,-1,sizeof(head)); 65 } 66 67 int main() 68 { 69 int n,m,i,j,k,x,y,z,cnt,now; 70 while ( scanf("%d",&n)!=EOF && n ) { 71 scanf("%d",&m); 72 init(); 73 while ( m-- ) { 74 scanf("%d%d",&x,&y); 75 addedge(x,y); 76 } 77 solve(n); 78 memset(vis,false,sizeof(vis)); 79 memset(out,0,sizeof(out)); 80 for ( i=1;i<=n;i++ ) { 81 for ( j=head[i];j!=-1;j=edge[j].nxt ) { 82 int v=edge[j].to; 83 x=belong[i]; 84 y=belong[v]; 85 if ( x!=y ) out[x]++; 86 } 87 } 88 cnt=0; 89 for ( i=1;i<=scc;i++ ) { 90 if ( out[i]==0 ) { 91 vis[i]=true; 92 cnt+=num[i]; 93 } 94 } 95 if ( cnt==0 ) { 96 printf("\n"); 97 continue; 98 } 99 now=0; 100 for ( i=1;i<=n;i++ ) { 101 if ( vis[belong[i]] ) { 102 printf("%d",i); 103 if ( ++now!=cnt ) printf(" "); 104 else printf("\n"); 105 } 106 } 107 } 108 return 0; 109 }
11.(POJ3114)http://poj.org/problem?id=3114
题意:给出n个城市,m条送信的渠道,u v w代表u城市送信到v城市需要w小时。同时如果两个城市属于一个国家,那么送信时间为0,在一个国家中的条件是所有城市相互之间可以送信到达。
分析:tarjan+spfa。首先利用tarjan进行缩点,因为可能存在重边所以距离存在数组中。缩点后构建新图。对于每个询问分别求一次spfa。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<queue> 5 #include<vector> 6 using namespace std; 7 const int maxn=550; 8 const int maxm=550*550; 9 const int inf=1e9; 10 struct edge{ 11 int to,nxt; 12 }edge[maxm]; 13 struct Edge{ 14 int v,cost; 15 Edge(int _v=0,int _cost=0):v(_v),cost(_cost) {} 16 }; 17 int head[maxn],tot; 18 int low[maxn],dfn[maxn],stack[maxn],belong[maxn],mp[maxn][maxn]; 19 int index,top; 20 int scc; 21 bool vis[maxn]; 22 int num[maxn],dis[maxn]; 23 vector<Edge>G[maxn]; 24 25 void addedge(int u,int v) 26 { 27 edge[tot].to=v; 28 edge[tot].nxt=head[u]; 29 head[u]=tot++; 30 } 31 32 void addedge_(int u,int v,int w) 33 { 34 G[u].push_back(Edge(v,w)); 35 } 36 37 void tarjan(int u) 38 { 39 int v; 40 low[u]=dfn[u]=++index; 41 stack[top++]=u; 42 vis[u]=true; 43 for ( int i=head[u];i!=-1;i=edge[i].nxt ) { 44 v=edge[i].to; 45 if ( !dfn[v] ) { 46 tarjan(v); 47 low[u]=min(low[u],low[v]); 48 } 49 else if ( vis[v] ) low[u]=min(low[u],dfn[v]); 50 } 51 if ( low[u]==dfn[u] ) { 52 scc++; 53 do { 54 v=stack[--top]; 55 vis[v]=false; 56 belong[v]=scc; 57 num[scc]++; 58 } 59 while ( v!=u ); 60 } 61 } 62 63 void solve(int N) 64 { 65 memset(dfn,0,sizeof(dfn)); 66 memset(vis,false,sizeof(vis)); 67 memset(num,0,sizeof(num)); 68 index=scc=top=0; 69 for ( int i=1;i<=N;i++ ) { 70 if ( !dfn[i] ) tarjan(i); 71 } 72 for ( int i=1;i<=scc;i++ ) G[i].clear(); 73 for ( int i=1;i<=N;i++ ) { 74 for ( int j=head[i];j!=-1;j=edge[j].nxt ) { 75 int v=edge[j].to; 76 int x=belong[i]; 77 int y=belong[v]; 78 if ( x!=y ) addedge_(x,y,mp[i][v]); 79 } 80 } 81 } 82 83 void init() 84 { 85 tot=0; 86 memset(head,-1,sizeof(head)); 87 } 88 89 int spfa(int s,int t) 90 { 91 int i,j,k; 92 for ( i=1;i<=scc;i++ ) dis[i]=inf; 93 queue<int>que; 94 memset(vis,false,sizeof(vis)); 95 vis[s]=true; 96 que.push(s); 97 dis[s]=0; 98 while ( !que.empty() ) { 99 int u=que.front(); 100 que.pop(); 101 vis[u]=false; 102 for ( int i=0;i<G[u].size();i++ ) { 103 int v=G[u][i].v; 104 if ( dis[v]>dis[u]+G[u][i].cost ) { 105 dis[v]=dis[u]+G[u][i].cost; 106 if ( !vis[v] ) { 107 que.push(v); 108 vis[v]=true; 109 } 110 } 111 } 112 } 113 return dis[t]; 114 } 115 116 int main() 117 { 118 int n,m,i,j,k,x,y,z,q,ans; 119 while ( scanf("%d%d",&n,&m)!=EOF && n ) { 120 init(); 121 for ( i=1;i<=n;i++ ) { 122 for ( j=1;j<=n;j++ ) mp[i][j]=inf; 123 } 124 while ( m-- ) { 125 scanf("%d%d%d",&x,&y,&z); 126 addedge(x,y); 127 mp[x][y]=min(mp[x][y],z); 128 } 129 solve(n); 130 scanf("%d",&q); 131 while ( q-- ) { 132 scanf("%d%d",&x,&y); 133 x=belong[x]; 134 y=belong[y]; 135 if ( x==y ) printf("0\n"); 136 else { 137 ans=spfa(x,y); 138 if ( ans==inf ) printf("Nao e possivel entregar a carta\n"); 139 else printf("%d\n",ans); 140 } 141 } 142 printf("\n"); 143 } 144 return 0; 145 }