拓扑排序

给定一个有向图G,对于任意一条有向边<Vi,Vj>,称Vi是Vj的直接前驱,Vj是Vi的直接后继,这种前驱与后继的关系具有传递性。

如果一个图的任何一个节点都具有反自反性,也就是说任何一个节点都不是自己的前驱或后继,那么这个有向图是拓扑有序的。

拓扑序列:

通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。离散数学中关于偏序和全序的定义:
  若集合X上的关系是R是自反的、反对称的和传递的,则称R是集合X上的偏序关系。
  设R是集合X上的偏序(Partial Order),如果对每个x,y属于X必有xRy 或 yRx,则称R是集合X上的全序关系。
  注意:
  ①若将图中顶点按拓扑次序排成一行,则图中所有的有向边均是从左指向右的。
  ②若图中存在有向环,则不可能使顶点满足拓扑次序。

  ③一个DAG的拓扑序列通常表示某种方案切实可行。

 

拓扑排序的一个应用是可以有向图中是否存在有向环。如果一个有向图存在环,那么必然存在结点不具有反自反性,因而相应的拓扑有序序列是不存在的。。

拓扑排序方法如下:

  (1)从有向图中选择一个没有前驱(即入度为0)的顶点并且输出它.
  (2)从网中删去该顶点,并且删去从该顶点发出的全部有向边.
  (3)重复上述两步,直到剩余的网中不再存在没有前趋的顶点为止.

 

 hdu 1285 确定比赛名次 链接:http://acm.hdu.edu.cn/showproblem.php?pid=1285

 解法:拓扑排序,找到一个人,没人能赢他,或者能赢他的人都已经输出,即他的入度为0,则输出此人。注意重边。。。

代码:

 

View Code 
 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4  using  namespace std;
 5  int n,m,sum;
 6  int map[ 501][ 501],degree[ 501],a[ 501],b[ 501];
 7  void topo( int x)
 8 {
 9      int i;
10     a[x]= 1;
11     b[++sum]=x;
12      for(i= 1;i<=n;i++)
13          if(!a[i]&&map[x][i])
14             degree[i]--;
15 }
16  int main()
17 {
18      int i,x,y;
19      while(~scanf( " %d%d ",&n,&m))
20     {
21         memset(map, 0, sizeof(map));
22         memset(degree, 0, sizeof(degree));
23         memset(a, 0, sizeof(a));
24          for(i= 0;i<m;i++)
25         {
26             scanf( " %d%d ",&x,&y);
27              if(map[x][y]== 0)
28             {
29                 map[x][y]= 1;
30                 degree[y]++;
31             }
32         }
33         sum= 0;
34          while(sum<n)
35         {
36              for(i= 1;i<=n;i++)
37             {
38                  if(degree[i]== 0&&a[i]== 0)
39                 {
40                     topo(i);
41                      break;
42                 }
43             }
44         }
45          for(i= 1;i<sum;i++)
46             printf( " %d  ",b[i]);
47         printf( " %d\n ",b[i]);
48     }
49      return  0;
50

 


hdu 2094 产生冠军 链接:http://acm.hdu.edu.cn/showproblem.php?pid=2094 

解法:这个跟上个题基本上一样,确定能不能产生冠军,能产生冠军即入度为0的点只有一个。也可以先用并查集来判断能不能形成连通图。

代码:

 

View Code 
 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include < string>
 5  using  namespace std;
 6  int nu,m,sum;
 7  int map[ 1001][ 1001];
 8  int degree[ 1001];
 9  char s[ 1001][ 51];
10  int a[ 1001];
11  int find( char *ss)
12 {
13      int i;
14      for(i= 0;i<nu;i++)
15     {
16          if(strcmp(ss,s[i])== 0)
17              return i;
18     }
19     strcpy(s[i],ss);
20      return nu++;
21 }
22  int topo()
23 {
24      int i,l= 0;
25      for(i= 0;i<nu;i++)
26          if(!degree[i])
27             l++;
28      return l;
29 }
30  int main()
31 {
32      char s1[ 101],s2[ 101];
33      int i,x,y;
34      while(~scanf( " %d ",&m))
35     {
36          if(m== 0)
37              break;
38         nu= 0;
39         memset(map, 0, sizeof(map));
40         memset(degree, 0, sizeof(degree));
41         memset(a, 0, sizeof(a));
42          for(i= 0;i<m;i++)
43         {
44             scanf( " %s %s ",s1,s2);
45             x=find(s1);
46             y=find(s2);
47              if(map[x][y]== 0)
48             {
49                 map[x][y]= 1;
50                 degree[y]++;
51             }
52         }
53          if(topo()== 1)
54         puts( " Yes ");
55          else
56         puts( " No ");
57     }
58      return  0;
59

 

 

 

POJ 1094 Sorting It All Out  链接: http://poj.org/problem?id=1094

代码:

 

 

View Code 
 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4  using  namespace std;
 5  int n,m,j,i,q,flag1;
 6  int map[ 100][ 100],degree[ 100],t[ 100],b[ 100];;
 7  int topo()
 8 {
 9      int i,j,f= 1,p,temp,k= 0;
10      for(i= 0;i<n;i++)
11         t[i]=degree[i];
12      for(i= 0;i<n;i++)
13     {
14         p= 0;
15          for(j= 0;j<n;j++)
16              if(t[j]== 0)
17             {
18                 p++;
19                 temp=j;
20             }
21          if(p== 0)
22              return  0;
23          if(p> 1)
24             f=- 1;
25         b[k++]=temp;
26         t[temp]=- 1;
27          for(j= 0;j<n;j++)
28              if(map[temp][j]!= 0)
29                 t[j]--;
30     }
31      return f;
32 }
33  int main()
34 {
35      while(~scanf( " %d %d ",&n,&m))
36     {
37         flag1= 0;
38          char c1,c2;
39          if(n== 0&&m== 0)
40              break;
41         memset(map, 0, sizeof(map));
42         memset(degree, 0, sizeof(degree));
43          for(i= 0;i<m;i++)
44         {
45             scanf( " %*c%c<%c ",&c1,&c2);
46             map[c1- ' A '][c2- ' A ']= 1;
47             degree[c2- ' A ']++;
48              if(flag1== 0)
49                 q=topo();
50              if(q== 1&&flag1== 0)
51             {
52                 printf( " Sorted sequence determined after %d relations:  ",i+ 1);
53                  for(j= 0;j<n;j++)
54                     printf( " %c ",b[j]+ ' A ');
55                 printf( " .\n ");
56                 flag1= 1;
57             }
58              if(q== 0&&flag1== 0)
59             {
60                 printf( " Inconsistency found after %d relations.\n ",i+ 1);
61                 flag1= 1;
62             }
63         }
64          if(flag1== 0)
65             printf( " Sorted sequence cannot be determined.\n ");
66     }
67      return  0;
68

 


POJ 2367 Genealogical tree  链接: http://poj.org/problem?id=2367

代码:

 

 

View Code 
 1 #include <iostream>
 2 #include <cstring>
 3 #include <cstdio>
 4  using  namespace std;
 5  const  int N= 102;
 6  int degree[N],map[N][N],a[N],b[N];
 7  int n,s;
 8  void topo( int x)
 9 {
10      int i;
11     a[x]= 1;
12     b[++s]=x;
13      for(i= 1;i<=n;i++)
14     {
15          if(!a[i]&&map[x][i])
16         {
17             degree[i]--;
18         }
19     }
20 }
21  int main()
22 {
23      int x,i;
24     scanf( " %d ",&n);    
25     memset(a, 0, sizeof( 0));
26     memset(degree, 0, sizeof(degree));
27     memset(map, 0, sizeof(map));
28      for(i= 1;i<=n;i++)
29     {
30          while( 1)
31         {
32             scanf( " %d ",&x);
33              if(x== 0)
34                  break;
35             map[i][x]= 1;
36             degree[x]++;
37         }
38     }
39     s= 0;
40      while(s<n)
41     {
42          for(i= 1;i<=n;i++)
43         {
44              if(degree[i]== 0&&a[i]!= 1)
45             {
46                 topo(i);
47             }
48         }
49     }
50      for(i= 1;i<n;i++)
51         printf( " %d  ",b[i]);
52     printf( " %d\n ",b[i]);
53      return  0;
54

 

 

poj 3687 Labeling Balls 链接:http://poj.org/problem?id=3687

题意:n个重量分别为1-n的小球,给定一些小球间的重量关系。 在符合重量关系的前提下,先输出编号小的球。

分析:拓扑排序,倒着来,注意一下重边。。。

代码:

 

 

View Code 
 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4  using  namespace std;
 5  const  int N= 210;
 6  int map[N][N],degree[N],a[N],b[N];
 7  int n, m;
 8  void topo()
 9 {
10      int i,j;
11     memset(a, 0, sizeof(a));
12      for(i=n;i> 0;i--)
13     {
14          int q=- 1;
15          for(j=n- 1;j>= 0;j--)
16              if(!a[j]&&degree[j]== 0)
17             {
18                 q=j;
19                  break;
20             }
21          if(q==- 1)
22         {
23             puts( " -1 ");
24              return;
25         }
26         a[q]= 1;
27         b[q]=i;
28          for(j= 0;j<n;j++)
29              if(map[q][j])
30                 degree[j]--;
31     }
32      for(i= 0;i<n- 1;i++)
33         printf( " %d  ",b[i]);
34     printf( " %d\n ",b[i]);
35 }
36  int main()
37 {
38      int t;
39     scanf( " %d ",&t);
40      while(t--)
41     {
42          int i;
43         scanf( " %d%d ",&n,&m);
44         memset(map, 0, sizeof(map));
45         memset(degree, 0, sizeof(degree));
46          for(i= 0;i<m;i++)
47         {
48              int a,b;
49             scanf( " %d%d ",&a,&b);
50             a--;
51             b--;
52              if(!map[b][a])
53                 degree[a]++;
54             map[b][a]= true;
55         }
56         topo();
57     }
58      return  0;
59

 

 

 

你可能感兴趣的:(排序)