图结构练习——判断给定图是否存在合法拓扑序列(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 }
View Code
常规思路,也是书上的思路,即判断是否存在入度为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 }
View Code

以上两种算法全部采用邻接矩阵的方法,但是采用邻接表的方法更具有一般性,这也是以上两种方法的不足之处。

 以下是用邻接表的方法:

 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 }
View Code

简化后的代码:

 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 }
View Code

 

 

测试数据:

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

你可能感兴趣的:(DFS)