专题训练之拓步排序
推荐几个博客:https://blog.csdn.net/dm_vincent/article/details/7714519 拓扑排序的原理及其实现
https://blog.csdn.net/u012860063/article/details/38017661 拓扑排序的几种写法
https://blog.csdn.net/shahdza/article/details/7779389 拓扑排序题集
1.基于DFS的拓扑排序:一般适用于数据较小并且需要判断有无环的情况
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int maxn=150; 6 int n,vis[maxn],topo[maxn],cnt; 7 bool g[maxn][maxn],flag; 8 9 void dfs(int u) 10 { 11 if ( vis[u]<0 ) { 12 flag=false; 13 return; 14 } 15 if ( vis[u]>0 ) return; 16 else vis[u]=-1; //表示当前还在访问中 17 for ( int v=1;flag&&v<=n;v++ ) { 18 if ( g[u][v] ) dfs(v); 19 } 20 topo[cnt--]=u; //保存下来 21 vis[u]=1; //访问完成 22 } 23 24 void toposort() 25 { 26 int i,j,k; 27 flag=true; 28 memset(vis,0,sizeof(vis)); //vis为0表示初从未访问过,为1表示已经已经访问完成,为-1表示当前还在访问中 29 cnt=n; 30 for ( int i=1;i<=n&&flag;i++ ) { 31 if ( vis[i]==0 ) dfs(i); 32 } 33 } 34 35 int main() 36 { 37 int i,m,x,y; 38 while ( scanf("%d%d",&n,&m)!=EOF ) { 39 memset(g,false,sizeof(g)); 40 while ( m-- ) { 41 scanf("%d%d",&x,&y); 42 g[x][y]=true; //g存储两个点的关系 43 } 44 toposort(); 45 for ( int i=1;i<n;i++ ) printf("%d",topo[i]); 46 printf("%d\n",topo[n]); 47 } 48 return 0; 49 }
模板题(HDOJ3342)http://acm.hdu.edu.cn/showproblem.php?pid=3342
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int maxn=110; 6 int vis[maxn],topo[maxn],n,cnt; 7 bool g[maxn][maxn],flag; 8 9 void dfs(int u) 10 { 11 if ( vis[u]<0 ) { 12 flag=false; 13 return; 14 } 15 if ( vis[u]>0 ) return; 16 else vis[u]=-1; 17 for ( int v=1;v<=n&&flag;v++ ) { 18 if ( g[u][v] ) dfs(v); 19 } 20 topo[cnt--]=u; 21 vis[u]=1; 22 } 23 24 void toposort() 25 { 26 memset(vis,0,sizeof(vis)); 27 flag=true; 28 cnt=n; 29 for ( int i=1;i<=n;i++ ) { 30 if ( vis[i]==0 ) dfs(i); 31 } 32 } 33 34 int main() 35 { 36 int m,i,j,k,x,y; 37 while ( scanf("%d%d",&n,&m)!=EOF && n ) { 38 memset(g,false,sizeof(g)); 39 for ( i=1;i<=m;i++ ) { 40 scanf("%d%d",&x,&y); 41 x++;y++; 42 g[x][y]=true; 43 } 44 toposort(); 45 if ( flag ) printf("YES\n"); 46 else printf("NO\n"); 47 } 48 return 0; 49 }
2.kahn算法求拓扑排序
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #include<queue> 6 using namespace std; 7 priority_queue<int,vector<int>,greater<int> >que; 8 const int maxn=505; 9 vector<int>G[maxn]; 10 int ans[maxn],ind[maxn],n; 11 12 void toposort() 13 { 14 while ( !que.empty() ) que.pop(); 15 for ( int i=1;i<=n;i++ ) { 16 if ( ind[i]==0 ) que.push(i); 17 } 18 int cnt=0; 19 while ( !que.empty() ) { 20 int tmp=que.top(); 21 que.pop(); 22 ans[++cnt]=tmp; 23 for ( int i=0;i<G[tmp].size();i++ ) { 24 ind[G[tmp][i]]--; 25 if ( ind[G[tmp][i]]==0 ) que.push(G[tmp][i]); 26 } 27 } 28 for ( int i=1;i<=n;i++ ) { 29 printf("%d",ans[i]); 30 if ( i==n ) printf("\n"); 31 else printf(" "); 32 } 33 } 34 35 int main() 36 { 37 int m,i,j,k,x,y; 38 while ( scanf("%d%d",&n,&m)!=EOF ) { 39 for ( i=1;i<=n;i++ ) G[i].clear(); 40 memset(ind,0,sizeof(ind)); 41 while ( m-- ) { 42 scanf("%d%d",&x,&y); 43 G[x].push_back(y); 44 ind[y]++; 45 } 46 toposort(); 47 } 48 return 0; 49 }
做法:
- 取出队头结点,存入ans数组。
- 然后“删除与该点相连的边”,即将从这个点出发到其他点的边所在的终点的入度-1;
- 如果-1以后,终点的入度变为了0,那么将终点的编号入队列。
- 不断循环直到队列为空/ans数组保存了n个元素
模板题(HDOJ1285)http://acm.hdu.edu.cn/showproblem.php?pid=1285
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #include<queue> 6 using namespace std; 7 priority_queue<int,vector<int>,greater<int> >que; 8 const int maxn=505; 9 vector<int>G[maxn]; 10 int ans[maxn],ind[maxn],n; 11 12 void toposort() 13 { 14 while ( !que.empty() ) que.pop(); 15 for ( int i=1;i<=n;i++ ) { 16 if ( ind[i]==0 ) que.push(i); 17 } 18 int cnt=1; 19 while ( !que.empty() ) { 20 int tmp=que.top(); 21 que.pop(); 22 ans[cnt++]=tmp; 23 for ( int i=0;i<G[tmp].size();i++ ) { 24 ind[G[tmp][i]]--; 25 if ( ind[G[tmp][i]]==0 ) que.push(G[tmp][i]); 26 } 27 } 28 for ( int i=1;i<=n;i++ ) { 29 printf("%d",ans[i]); 30 if ( i==n ) printf("\n"); 31 else printf(" "); 32 } 33 } 34 35 int main() 36 { 37 int m,i,j,k,x,y; 38 while ( scanf("%d%d",&n,&m)!=EOF ) { 39 for ( i=1;i<=n;i++ ) G[i].clear(); 40 memset(ind,0,sizeof(ind)); 41 while ( m-- ) { 42 scanf("%d%d",&x,&y); 43 G[x].push_back(y); 44 ind[y]++; 45 } 46 toposort(); 47 } 48 return 0; 49 }
总结:拓扑序列是由某个集合上的一个偏序得到该集合上的一个全序。题目往往给定一个偏序。而全序往往是按照一定的要求才能得到的(这样的要求往往体现在优先队列中需要维护的变量上)。一般是要求字典序最小,有时候也会有很多附加条件,都是为了得到这个全序。
练习题:
1.(HDOJ4857)http://acm.hdu.edu.cn/showproblem.php?pid=4857
分析:该题字典序最小不同,该题的目的是不断想办法使当前所有未访问过的节点中编号最小的先出来。即存在6-4-1和3-9-2,因为当前存在的编号最小的是1,所有先让6 4 1先出来,6 4出来的目的是使1尽早出来。因此该题只需要反向建图,每次取出编号大的保存在ans数组的尾端。最关键的一点是小的头部不一定排在前面,而大的尾部一定排在后面
推荐一个解释较好的博客:https://blog.csdn.net/u012861385/article/details/38059515
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #include<queue> 6 using namespace std; 7 const int maxn=30010; 8 vector<int>G[maxn]; 9 int ans[maxn],ind[maxn],n; 10 11 void toposort() 12 { 13 priority_queue<int>que; 14 while ( !que.empty() ) que.pop(); 15 for ( int i=1;i<=n;i++ ) { 16 if ( ind[i]==0 ) que.push(i); 17 } 18 int cnt=n; 19 while ( !que.empty() ) { 20 int tmp=que.top(); 21 que.pop(); 22 ans[cnt--]=tmp; 23 for ( int i=0;i<G[tmp].size();i++ ) { 24 ind[G[tmp][i]]--; 25 if ( ind[G[tmp][i]]==0 ) que.push(G[tmp][i]); 26 } 27 } 28 for ( int i=1;i<=n;i++ ) { 29 printf("%d",ans[i]); 30 if ( i==n ) printf("\n"); 31 else printf(" "); 32 } 33 } 34 35 int main() 36 { 37 int m,i,j,k,x,y,T; 38 scanf("%d",&T); 39 while ( T-- ) { 40 scanf("%d%d",&n,&m); 41 for ( i=1;i<=n;i++ ) G[i].clear(); 42 memset(ind,0,sizeof(ind)); 43 while ( m-- ) { 44 scanf("%d%d",&x,&y); 45 G[y].push_back(x); 46 ind[x]++; 47 } 48 toposort(); 49 } 50 return 0; 51 }
2.(HDOJ2647)http://acm.hdu.edu.cn/showproblem.php?pid=2647
题意:有n个人,m条要求,每条要求的内容为(x,y),即x要求自己的奖金比y高,求老板最少需要发多少奖金,每人至少888,当有矛盾时输出-1
分析:当最后ans数组中保存的数cnt少于n时说明出现了环,此时矛盾输出-1。可以设置一个rk数组表示每个人的“等级”,最后第i个人获得的奖金为888+rk[i]。rk数组初始化为-1,刚开始入度为0的点rk为0,对于边(u,v)上的v点来说,rk[v]=max(rk[v],rk[u]+1)。而对于整个图来说需要反向建图。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #include<queue> 6 using namespace std; 7 const int maxn=1e4+10; 8 typedef long long ll; 9 vector<int>G[maxn]; 10 int ans[maxn],ind[maxn],n,rk[maxn]; 11 12 void toposort() 13 { 14 queue<int>que; 15 while ( !que.empty() ) que.pop(); 16 for ( int i=1;i<=n;i++ ) { 17 if ( ind[i]==0 ) { 18 que.push(i); 19 rk[i]=0; 20 } 21 } 22 int cnt=0; 23 while ( !que.empty() ) { 24 int tmp=que.front(); 25 que.pop(); 26 ans[++cnt]=tmp; 27 for ( int i=0;i<G[tmp].size();i++ ) { 28 int v=G[tmp][i]; 29 ind[v]--; 30 rk[v]=max(rk[v],rk[tmp]+1); 31 if ( ind[v]==0 ) que.push(v); 32 } 33 } 34 if ( cnt<n ) printf("-1\n"); 35 else { 36 ll sum=0; 37 for ( int i=1;i<=n;i++ ) sum+=rk[i]; 38 sum=(ll)888*n+sum; 39 printf("%lld\n",sum); 40 } 41 } 42 43 int main() 44 { 45 int m,i,j,k,x,y; 46 while ( scanf("%d%d",&n,&m)!=EOF ) { 47 for ( i=1;i<=n;i++ ) G[i].clear(); 48 memset(ind,0,sizeof(ind)); 49 memset(rk,-1,sizeof(rk)); 50 while ( m-- ) { 51 scanf("%d%d",&x,&y); 52 G[y].push_back(x); 53 ind[x]++; 54 } 55 toposort(); 56 } 57 return 0; 58 }
3.(POJ3687)http://poj.org/problem?id=3687
题意:有n个球,每个球的重量为i(从1到n),现在要给每个球贴标签(编号),没有两个编号是一样的;有m个要求,要求(x,y)表示第x个球的标签要小于第y个球。
分析:大致同HDOJ4857,需要反向建图,因为该题也是需要当前重量最小的球尽量靠前
注意:输出一行。第i个表示的是第i个球的编号,而不是拓步排序中的顺序
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #include<queue> 6 using namespace std; 7 const int maxn=205; 8 vector<int>G[maxn]; 9 int ans[maxn],ind[maxn],n,pos[maxn]; 10 11 void toposort() 12 { 13 priority_queue<int>que; 14 while ( !que.empty() ) que.pop(); 15 for ( int i=1;i<=n;i++ ) { 16 if ( ind[i]==0 ) que.push(i); 17 } 18 int cnt=n; 19 while ( !que.empty() ) { 20 int tmp=que.top(); 21 que.pop(); 22 ans[cnt--]=tmp; 23 for ( int i=0;i<G[tmp].size();i++ ) { 24 ind[G[tmp][i]]--; 25 if ( ind[G[tmp][i]]==0 ) que.push(G[tmp][i]); 26 } 27 } 28 if ( cnt>0 ) { 29 printf("-1\n"); 30 return; 31 } 32 for ( int i=1;i<=n;i++ ) { 33 pos[ans[i]]=i; 34 } 35 for ( int i=1;i<=n;i++ ) { 36 printf("%d",pos[i]); 37 if ( i==n ) printf("\n"); 38 else printf(" "); 39 } 40 } 41 42 int main() 43 { 44 int m,i,j,k,x,y,T; 45 scanf("%d",&T); 46 while ( T-- ) { 47 scanf("%d%d",&n,&m); 48 for ( i=1;i<=n;i++ ) G[i].clear(); 49 memset(ind,0,sizeof(ind)); 50 while ( m-- ) { 51 scanf("%d%d",&x,&y); 52 G[y].push_back(x); 53 ind[x]++; 54 } 55 toposort(); 56 } 57 return 0; 58 }
4.(POJ3553)http://poj.org/problem?id=3553
题意:有n项工作,每项工作有一个持续时间和截至时间。有m条关系,每条关系(x,y)表示任务x一定要在任务y前进行。对于任务j来说当前的完成时间记做cj,现使得所有任务的max[cj-dj,0]最小,求任务进行的顺序
分析:由偏序得到全序的条件是max[cj-dj,0]最小,那么我们可以将优先队列中的顺序按照d从小到大进行排列。假设当前的时间为now,当前最小的d对应的任务为i,持续时间为pi,截至时间为di,此时有差值value1=abs(now+pi-di)。若此时不进行任务i,而进行其他任务j,下次再进行任务i对于任务i来说value2=abs(now+pj+pi-di)一定是比value1要更大的。所有对于当前的局面来说每次取di最小的可以得到最优的答案
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #include<queue> 6 #include<cmath> 7 using namespace std; 8 const int maxn=50010; 9 struct node{ 10 int p; 11 int d; 12 int id; 13 node(int _p=0,int _d=0,int _id=0):p(_p),d(_d),id(_id) {} 14 bool operator < (const node &r)const 15 { 16 return d>r.d; 17 } 18 }arr[maxn]; 19 vector<int>G[maxn]; 20 int ans[maxn],ind[maxn],n; 21 22 void toposort() 23 { 24 int num,now; 25 num=now=0; 26 priority_queue<node>que; 27 while ( !que.empty() ) que.pop(); 28 for ( int i=1;i<=n;i++ ) { 29 if ( ind[i]==0 ) que.push(node(arr[i].p,arr[i].d,arr[i].id)); 30 } 31 int cnt=0; 32 while ( !que.empty() ) { 33 node tmp=que.top(); 34 que.pop(); 35 int u=tmp.id; 36 now+=arr[u].p; 37 num=max(num,abs(now-arr[u].p)); 38 ans[++cnt]=u; 39 for ( int i=0;i<G[u].size();i++ ) { 40 int v=G[u][i]; 41 ind[v]--; 42 if ( ind[v]==0 ) que.push(node(arr[v].p,arr[v].d,v)); 43 } 44 } 45 for ( int i=1;i<=n;i++ ) { 46 printf("%d\n",ans[i]); 47 } 48 } 49 50 int main() 51 { 52 int m,i,j,k,x,y; 53 while ( scanf("%d",&n)!=EOF ) { 54 for ( i=1;i<=n;i++ ) G[i].clear(); 55 memset(ind,0,sizeof(ind)); 56 for ( i=1;i<=n;i++ ) { 57 scanf("%d%d",&arr[i].p,&arr[i].d); 58 arr[i].id=i; 59 } 60 scanf("%d",&m); 61 while ( m-- ) { 62 scanf("%d%d",&x,&y); 63 G[x].push_back(y); 64 ind[y]++; 65 } 66 toposort(); 67 } 68 return 0; 69 }
5.(POJ2762)http://poj.org/problem?id=2762
题意:给定的图中是否满足对于任意两点(u,v),总有u到v或者v到u
分析:强连通分量。先缩点,然后判断出入度为1的点是否都只有一个.因为当且仅当图为一条链时才满足条件,如果出现分叉则一定不满足条件。
做法2:先缩点再进行toposort,当每次弹出队列中最前面的元素时进行判断队列中是否还有其他元素,如果有则不成立。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #include<queue> 6 using namespace std; 7 const int maxn=1050; 8 const int maxm=15000; 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]; 18 vector<int>G[maxn]; 19 int ans[maxn],ind[maxn],n,out[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 tarjan(int u) 29 { 30 int v; 31 low[u]=dfn[u]=++index; 32 stack[top++]=u; 33 vis[u]=true; 34 for ( int i=head[u];i!=-1;i=edge[i].nxt ) { 35 v=edge[i].to; 36 if ( !dfn[v] ) { 37 tarjan(v); 38 low[u]=min(low[u],low[v]); 39 } 40 else if ( vis[v] ) low[u]=min(low[u],dfn[v]); 41 } 42 if ( low[u]==dfn[u] ) { 43 scc++; 44 do { 45 v=stack[--top]; 46 vis[v]=false; 47 belong[v]=scc; 48 num[scc]++; 49 } 50 while ( v!=u ); 51 } 52 } 53 54 void solve(int N) 55 { 56 memset(dfn,0,sizeof(dfn)); 57 memset(vis,false,sizeof(vis)); 58 memset(num,0,sizeof(num)); 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,z,T; 74 scanf("%d",&T); 75 while ( T-- ) { 76 scanf("%d%d",&n,&m); 77 init(); 78 for ( i=0;i<m;i++ ) { 79 scanf("%d%d",&x,&y); 80 addedge(x,y); 81 } 82 solve(n); 83 for ( i=1;i<=scc;i++ ) G[i].clear(); 84 memset(ind,0,sizeof(ind)); 85 memset(out,0,sizeof(out)); 86 for ( i=1;i<=n;i++ ) { 87 for ( j=head[i];j!=-1;j=edge[j].nxt ) { 88 int v=edge[j].to; 89 if ( belong[i]!=belong[v] ) { 90 ind[belong[v]]++; 91 out[belong[i]]++; 92 } 93 } 94 } 95 int now1,now2; 96 now1=now2=0; 97 for ( i=1;i<=scc;i++ ) { 98 if ( ind[i]==0 ) now1++; 99 if ( out[i]==0 ) now2++; 100 } 101 if ( now1==1 && now2==1 ) printf("Yes\n"); 102 else printf("No\n"); 103 } 104 return 0; 105 }
6.(HDOJ1811)http://acm.hdu.edu.cn/showproblem.php?pid=1811
分析:并查集+toposort。首先并查集的作用在于对于那些rating相等的人他们之间的排名一定是确定的,这时候可以考虑“缩点”,将那些rating相等的人缩成一个点,此时要注意不能边缩点边建图,而是需要先全部缩点完成后再建图,否则建图会有误。同时在整个toposort的过程中,对象的编号是并查集完成后的编号,对象的数目是缩点后的数目。对于三种情况的判定:当出现环时则为矛盾(此时cnt<num)。判断信息是否完备的条件同POJ2762,当只有一条链时才能说明信息完备,此时判断每个点弹出队列后是否还有元素在队列当中。其他情况输出OK。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<queue> 5 #include<vector> 6 using namespace std; 7 const int maxn=100005; 8 vector<int>G[maxn]; 9 int n,ind[maxn],cnt,F[maxn],sum; 10 struct node{ 11 int a; 12 int b; 13 char c; 14 }arr[maxn*2]; 15 16 int find(int x) 17 { 18 if ( F[x]==-1 ) return x; 19 return F[x]=find(F[x]); 20 } 21 22 void merge(int x,int y) 23 { 24 int dx,dy; 25 dx=find(x); 26 dy=find(y); 27 if ( dx!=dy ) F[dx]=dy; 28 } 29 30 void toposort() 31 { 32 queue<int>que; 33 bool flag=false; 34 cnt=0; 35 for ( int i=0;i<n;i++ ) { 36 if ( ind[i]==0 && F[i]==-1 ) que.push(i); //F[i]==-1的判断条件不能遗漏 37 } 38 while ( !que.empty() ) { 39 40 int tmp=que.front(); 41 que.pop(); 42 if ( !que.empty() ) flag=true; //判断是否为直链 43 cnt++; 44 for ( int i=0;i<G[tmp].size();i++ ) { 45 ind[G[tmp][i]]--; 46 if ( ind[G[tmp][i]]==0 ) que.push(G[tmp][i]); 47 } 48 } 49 if ( cnt<sum ) printf("CONFLICT\n"); 50 else if ( flag ) printf("UNCERTAIN\n"); 51 else printf("OK\n"); 52 } 53 54 int main() 55 { 56 int m,i,j,k,fy,fx; 57 char s; 58 while ( scanf("%d%d",&n,&m)!=EOF ) { 59 memset(ind,0,sizeof(ind)); 60 memset(F,-1,sizeof(F)); 61 for ( i=0;i<n;i++ ) G[i].clear(); 62 for ( i=1;i<=m;i++) { 63 scanf("%d %c %d",&arr[i].a,&arr[i].c,&arr[i].b); 64 if ( arr[i].c=='=' ) merge(arr[i].a,arr[i].b); 65 } 66 for ( i=1;i<=m;i++ ) { 67 fx=find(arr[i].a); 68 fy=find(arr[i].b); 69 if ( arr[i].c=='=' ) continue; 70 else if ( arr[i].c=='>' ) { 71 G[fx].push_back(fy); 72 ind[fy]++; 73 } 74 else if ( arr[i].c=='<' ) { 75 G[fy].push_back(fx); 76 ind[fx]++; 77 } 78 } 79 sum=0; 80 for ( i=0;i<n;i++ ) { 81 if ( F[i]==-1 ) sum++; 82 } 83 toposort(); 84 } 85 return 0; 86 }
7.(HDOJ3357)http://acm.hdu.edu.cn/showproblem.php?pid=3357
题意:有n个公司和m笔交易,每笔交易(x,y)表示x公司买y公式的股份,当一个公司试图购买其父公司的股份时交易便会被停止,求停止的交易数
分析:本来应该每次加一条边然后进行一次拓扑排序判断有无环,但是复杂度太大。所以才用floyd来判环。具体做法是每个输入(x,y),先判断y是否为x的父公司(即y购买过x的股份),同时需要判断x是否等于y(容易遗漏)。若不是则通过floyd更新一次,但是每次floyd不能进行三层循环。对于添加的边(x-y)来说可以更新(i-x-y),(x-y-j),(i-x-y-j)三种边,这时候可以用floyd两层循环搞定(最外层k只能为x/y)。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int maxn=250; 6 bool d[maxn][maxn]; 7 int n; 8 9 void floyd(int x,int y) 10 { 11 int i,j,k; 12 for ( i=1;i<=n;i++ ) { 13 if ( !d[i][x] ) continue; 14 for ( j=1;j<=n;j++ ) { 15 if ( d[i][j] || !d[x][j] ) continue; 16 d[i][j]=true; 17 } 18 } 19 for ( i=1;i<=n;i++ ) { 20 if ( !d[i][y] ) continue; 21 for ( j=1;j<=n;j++ ) { 22 if ( d[i][j] || !d[y][j] ) continue; 23 d[i][j]=true; 24 } 25 } 26 } 27 28 int main() 29 { 30 int i,j,k,x,y,z,m,h=0,ans; 31 while ( scanf("%d%d",&n,&m)!=EOF && (n+m) ) { 32 memset(d,false,sizeof(d)); 33 ans=0; 34 for ( i=1;i<=m;i++ ) { 35 scanf("%d%d",&x,&y); 36 if ( d[x][y] ) continue; 37 if ( d[y][x] || x==y ) { 38 ans++; 39 continue; 40 } 41 d[x][y]=true; 42 floyd(x,y); 43 } 44 printf("%d. %d\n",++h,ans); 45 } 46 return 0; 47 }
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int maxn=250; 6 bool d[maxn][maxn]; 7 int n; 8 9 void floyd(int x,int y) 10 { 11 int i,j,k; 12 for ( i=1;i<=n;i++ ) { 13 if ( d[i][x] ) d[i][y]=true; 14 if ( d[y][i] ) d[x][i]=true; 15 } 16 for ( i=1;i<=n;i++ ) { 17 if ( !d[i][x] ) continue; 18 for ( j=1;j<=n;j++ ) { 19 if ( !d[y][j] || d[i][j] ) continue; 20 d[i][j]=true; 21 } 22 } 23 } 24 25 int main() 26 { 27 int i,j,k,x,y,z,m,h=0,ans; 28 while ( scanf("%d%d",&n,&m)!=EOF && (n+m) ) { 29 memset(d,false,sizeof(d)); 30 ans=0; 31 for ( i=1;i<=m;i++ ) { 32 scanf("%d%d",&x,&y); 33 if ( d[y][x] || x==y ) { 34 ans++; 35 continue; 36 } 37 if ( d[x][y] ) continue; 38 d[x][y]=true; 39 floyd(x,y); 40 } 41 printf("%d. %d\n",++h,ans); 42 } 43 return 0; 44 }