图结构练习——判断给定图是否存在合法拓扑序列(dfs算法(第一个代码),邻接矩阵(前两个代码),邻接表(第三个代码))
图结构练习——判断给定图是否存在合法拓扑序列
Time Limit: 1000ms Memory limit: 65536K 有疑问?点这里^_^
题目描述
给定一个有向图,判断该有向图是否存在一个合法的拓扑序列。
输入
输入包含多组,每组格式如下。
第一行包含两个整数n,m,分别代表该有向图的顶点数和边数。(n<=10)
后面m行每行两个整数a b,表示从a到b有一条有向边。
输出
若给定有向图存在合法拓扑序列,则输出YES;否则输出NO。
示例输入
1 0 2 2 1 2 2 1
示例输出
YES NO
网上看到有人的思路很巧妙,忍不住就稍作修改,贴了上来:http://www.cnblogs.com/luyingfeng/archive/2013/07/29/3223090.html
第一种思路是直接判断环是否存在的思路,这种思路采用dfs算法,是非常规思路:
1 #include<stdio.h> 2 #include<string.h> 3 #include<stdlib.h> 4 int vis[11]; 5 int m,n,u,v; 6 int map[11][11]; 7 int dfs(int u)//深度优先搜索遍历 8 { 9 vis[u]=-1;//标记-1,正在访问 10 for(v=1; v<=m; v++) 11 { 12 if(map[u][v])//如果v是u的后继元素 13 { 14 if(vis[v]<0)//如果v元素是正在访问中的状态 15 return 0;//存在自环 16 //如果存在的不是自环 17 if(!vis[v]&&!dfs(v))//v元素还没有被访问而且v的后继元素和前面的正在访问的元素构成了回路 18 return 0; 19 } 20 } 21 //若果u的后继结点全部没有形成自回路或者是回路 22 vis[u]=1;//标记1,访问完了,删除此节点 23 return 1; 24 } 25 int toposort() 26 { 27 for(u=1; u<=m; u++) 28 { 29 if(!vis[u])//如果u节点还没有访问 30 { 31 if(!dfs(u))//如果dfs的返回值是0,强制退出排序函数toposort,表明存在环 32 return 0; 33 } 34 } 35 return 1;//如果u从1到m全部遍历完 36 } 37 int main() 38 { 39 while(~scanf("%d %d",&m,&n)&&(m+n!=0)) 40 { 41 memset(vis,0,sizeof(vis)); 42 memset(map,0,sizeof(map)); 43 for(int i=0; i<=n-1; i++) 44 { 45 scanf("%d %d",&u,&v); 46 map[u][v]=1; 47 } 48 if(toposort())//如果返回值是1,不存在回路,无论是自回路或者是回路 49 printf("YES\n"); 50 else printf("NO\n");//如果返回值是0 51 } 52 return 0; 53 }
常规思路,也是书上的思路,即判断是否存在入度为0的节点,若在循环未结束的时候,就找不到入度为0的节点,则必存在环
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 int map[51][51],count[51]; 5 int flag; 6 int u,v,vis; 7 int main() 8 { 9 int m,n; 10 while(~scanf("%d %d",&m,&n)&&(n!=0||m!=0)) 11 { 12 memset(map,0,sizeof(map)); 13 memset(count,0,sizeof(count));//每个节点的入度初始为0; 14 vis=0;//标记变量,用于帮助最后输出 15 for(int i=0; i<=n-1; i++) 16 { 17 scanf("%d %d",&u,&v); 18 map[u][v]=1; 19 count[v]++; 20 } 21 for(int i=0; i<=m-1; i++) 22 { 23 int flag=0; 24 for(u=1; u<=m; u++) 25 { 26 if(count[u]==0) 27 { 28 flag=1;//只要有入度为0的点就变为1 29 count[u]--;//删除掉这个节点 30 for(v=1; v<=m; v++)//凡是与该节点有关的所有结点入度去掉1; 31 { 32 if(map[u][v]) 33 { 34 count[v]--; 35 } 36 } 37 break; 38 } 39 } 40 if(flag==0)//如果没有入度为0的点,就代表没有拓扑序列 41 { 42 vis=1; 43 break; 44 } 45 } 46 if(vis==0) 47 printf("YES\n"); 48 else printf("NO\n"); 49 } 50 return 0; 51 }
以上两种算法全部采用邻接矩阵的方法,但是采用邻接表的方法更具有一般性,这也是以上两种方法的不足之处。
以下是用邻接表的方法:
1 #include<stdio.h> 2 #include<string.h> 3 #include<stdlib.h> 4 struct vode 5 { 6 int v; 7 struct vode *next; 8 }; 9 struct vode *f[1001]; 10 int count[1001]; 11 int stack[101],top=1; 12 int m,n; 13 int topsort() 14 { 15 int i,sum=0; 16 int flag; 17 while(1) 18 { 19 flag=0; 20 for(i=1;i<=m;i++) 21 { 22 if(count[i]==0) 23 { 24 stack[top++]=i; 25 sum++; 26 count[i]--;//删除i节点 27 struct vode *p; 28 for(p=f[i];p!=NULL;p=p->next)//把i节点的邻接点的入度都减1 29 { 30 int t=p->v; 31 count[t]--; 32 } 33 flag=1; 34 } 35 if(flag==1)break;//如果找到了入度是0的节点,从头开始再找入度是0的节点 36 } 37 if(flag==0)//循环完了之后再也找不到入度是0的节点,这时候可能会有环,也可能没有环 38 { 39 break; 40 } 41 } 42 return sum; 43 } 44 int main() 45 { 46 while(scanf("%d%d",&m,&n)!=EOF) 47 { 48 int i; 49 memset(count,0,sizeof(count)); 50 for(i=0;i<=m;i++) 51 f[i]=NULL; 52 for(i=1;i<=n;i++) 53 { 54 int u,v; 55 scanf("%d%d",&u,&v); 56 count[v]++; 57 struct vode *p; 58 p=(struct vode *)malloc(sizeof(struct vode)); 59 p->v=v; 60 p->next=f[u]; 61 f[u]=p; 62 } 63 64 int sum=topsort(); 65 if(sum!=m)printf("NO\n"); 66 else printf("YES\n"); 67 /* 68 通过这个循环可以得到最终每个节点的入度,如果是合法的拓扑序列,最终的结果应当都是-1 69 for(i=1;i<=m;i++) 70 printf("%d ",count[i]); 71 printf("\n"); 72 通过下面这个循环可以得到最终一个拓扑序列(如果合法),这个序列可能只是合法序列当中的一个 73 for(i=1;i<=m;i++) 74 printf("%d ",stack[i]); 75 printf("\n"); 76 */ 77 } 78 return 0; 79 }
简化后的代码:
1 #include<stdio.h> 2 #include<string.h> 3 #include<stdlib.h> 4 struct vode 5 { 6 int v; 7 struct vode *next; 8 }; 9 struct vode *f[300]; 10 int m,n; 11 int rudu[300]; 12 int stack[300],top; 13 void hs() 14 { 15 int i,k=0; 16 while(k=!k) 17 for(i=1;i<=m;i++) 18 if(rudu[i]==0) 19 { 20 k=0; 21 rudu[i]--; 22 stack[top++]=i; 23 struct vode *p; 24 for(p=f[i];p!=NULL;p=p->next) 25 rudu[p->v]--; 26 break; 27 } 28 } 29 int main() 30 { 31 while(scanf("%d%d",&m,&n)!=EOF) 32 { 33 memset(f,0,sizeof(f)); 34 memset(rudu,0,sizeof(rudu)); 35 memset(stack,0,sizeof(stack)); 36 top=0; 37 int i; 38 for(i=1;i<=n;i++) 39 { 40 int u,v; 41 scanf("%d%d",&u,&v); 42 rudu[v]++; 43 struct vode *p; 44 p=(struct vode *)malloc(sizeof(struct vode)); 45 p->v=v; 46 p->next=f[u]; 47 f[u]=p; 48 } 49 hs(); 50 /*//下面的循环可以显示出拓扑序列 51 for(i=0;i<=top-1;i++) 52 printf("%d ",stack[i]); 53 printf("\n"); 54 */ 55 if(top==m)printf("YES\n"); 56 else printf("NO\n"); 57 } 58 return 0; 59 }
测试数据:
5 4
1 3
2 3
3 4
3 5
输出:
YES
-1 -1 -1 -1 -1
1 2 3 4 5
测试连接:http://wenku.baidu.com/view/bb32ee2e4b73f242336c5f53.html