拓扑排序

拓扑排序是将一个DAG图的所有顶点排成一个线性序列,使得图中任意一对顶点(u,v)
若存在边,则u在线性排列中出现在v之前的排列方式
(1)算法描述:

(a)从网中选择一个入度为零的顶点输出;

(b)删除该顶点及其于该点有关的所有边;

(c)是否还有入度为零的顶点?若有,执行(a),否则结束。

用邻接矩阵时间复杂度为O(N*N),邻接表的时间复杂度为O(E)

 http://acm.hdu.edu.cn/showproblem.php?pid=1285

即输出字典序最小的拓扑排序

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <stack>
 4 #include <algorithm>
 5 using namespace std;
 6 const int N = 555;
 7 int g[N][N];
 8 int ans[N],in[N];
 9 void topSort(int n, int m)
10 {
11     int i,j,k;
12     for(i=1; i<=n; ++i)
13         for(j=1; j<=n; ++j)
14             if(g[i][j])
15                 in[j]++;//计算每个点的入度
16     for(i=1; i<=n; ++i)
17     {
18         for(j=1; in[j]&&j<=n; ++j)//找到入度为0的点
19                 NULL;
20         in[j] = -1;
21         ans[i]=j;//记录
22         for(k=1; k<=n; ++k)
23             if(g[j][k])//删边
24                 in[k]--;
25     }    
26 }
27 int main()
28 {
29     int n,m,i,u,v;
30     while(scanf("%d%d",&n,&m)!=EOF)
31     {
32         memset(g,0,sizeof(g));
33         memset(in,0,sizeof(in));
34         for(i=0; i<m; ++i)
35         {
36             scanf("%d%d",&u,&v);
37             g[u][v] = 1;
38         }
39         topSort(n,m);
40         for(i=1; i<=n; ++i)
41         {
42             if(i==1)
43                 printf("%d",ans[i]);
44             else
45                 printf(" %d",ans[i]);
46         }
47         puts("");
48     }
49     return 0;
50 }
View Code

 http://acm.hdu.edu.cn/showproblem.php?pid=2647

给定n个人,m条边

边u->v表示  u的工资要比v高, 每个人的基础工资为888,问总共的工资最少是多少。

可以看做是这么一种情况,把整个图分为若干层,层内的工资相等,层与层之间的工资相差1.

u->v表示u的工资比v高,那么要先确定v才能确定u,那么可以反向建图。

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <vector>
 4 #include <stack>
 5 using namespace std;
 6 const int N = 10000 + 10;
 7 vector<int> g[N];
 8 stack<int> st;
 9 int in[N],add[N],cnt;
10 inline int max(const int &a, const int &b)
11 {
12     return a < b ? b : a;
13 }
14 void topSort(int n, int m)
15 {    
16     int u,i;
17     for(i=1; i<=n; ++i)
18         if(in[i]==0)
19             st.push(i);
20     while(!st.empty())
21     {
22         u = st.top();//u出栈,表示u的工资已经确定,那么可以推出u指向的顶点的工资
23         cnt++;
24         st.pop();
25         for(i=0; i<g[u].size(); ++i)
26         {
27             --in[g[u][i]];
28             if(in[g[u][i]]==0)
29                 st.push(g[u][i]);
30             add[g[u][i]] = max(add[u]+1,add[g[u][i]]);//如果当前的值比前驱加上一的值还小就要更新当前点的值 
31                 
32         }
33     }
34 }
35 void init(int n)
36 {
37     cnt = 0;
38     for(int i=1; i<=n; ++i)
39     {
40         g[i].clear();
41         add[i] = in[i] = 0;
42     }
43 }
44 int main()
45 {
46     int n,m,i,u,v;
47     while(scanf("%d%d",&n,&m)!=EOF)
48     {
49         init(n);
50         for(i=0; i<m; ++i)
51         {
52             scanf("%d%d",&u,&v);
53             g[v].push_back(u);//反向建图
54             in[u]++;
55         }
56         topSort(n,m);
57         int ans = 0;
58         if(cnt==n)
59         {
60             for(i=1; i<=n; ++i)
61                 ans += add[i] + 888;
62             printf("%d\n",ans);
63         }
64         else
65             printf("-1\n");
66     }
67 }
View Code

 http://acm.hdu.edu.cn/showproblem.php?pid=2647

给定n个人,m条边

判断图是不是DAG图。 拓扑排序O(E), bellman O(VE)   , 使用邻接表的dfs O(V+E) ,floyd O(N*N*N)

其余代码见 http://www.cnblogs.com/justPassBy/p/4342599.html

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <vector>
 4 #include <stack>
 5 using namespace std;
 6 const int N = 10000 + 10;
 7 vector<int> g[N];
 8 stack<int> st;
 9 int in[N],add[N],cnt;
10 inline int max(const int &a, const int &b)
11 {
12     return a < b ? b : a;
13 }
14 void topSort(int n, int m)
15 {    
16     int u,i;
17     for(i=1; i<=n; ++i)
18         if(in[i]==0)
19             st.push(i);
20     while(!st.empty())
21     {
22         u = st.top();
23         cnt++;
24         st.pop();
25         for(i=0; i<g[u].size(); ++i)
26         {
27             --in[g[u][i]];
28             if(in[g[u][i]]==0)
29                 st.push(g[u][i]);
30         
31                 
32         }
33     }
34 }
35 void init(int n)
36 {
37     cnt = 0;
38     for(int i=1; i<=n; ++i)
39     {
40         g[i].clear();
41         add[i] = in[i] = 0;
42     }
43 }
44 int main()
45 {
46     int n,m,i,u,v;
47     while(scanf("%d%d",&n,&m)!=EOF)
48     {
49         if(n==0 && m==0)
50             break;
51         init(n);
52         for(i=0; i<m; ++i)
53         {
54             scanf("%d%d",&u,&v);
55             u++;
56             v++;
57             g[u].push_back(v);
58             in[v]++;
59         }
60         topSort(n,m);
61         if(cnt==n)//能生成n个顶点的序列,说明是DAG图
62         {
63             puts("YES");
64         }
65         else
66             puts("NO");
67     }
68 }
View Code

 

http://acm.hdu.edu.cn/showproblem.php?pid=1811

根据所给的信息判定能否确定唯一排名。

题目的难点在于=号的处理。  我们可以利用并查集将相等的结点并在一个集合里。然后再使用拓扑排序就可以了。

1、如果每次入栈的结点大于1个,那么拓扑排序不唯一。 这很好理解

2、如果出栈的顶点个数小于总顶点个数,那么存在回路。

  1 #include <stdio.h>
  2 #include <string.h>
  3 #include <vector>
  4 #include <stack>
  5 using namespace std;
  6 const int N = 10000 + 10;
  7 int u[N],v[N];
  8 int in[N],father[N];
  9 char str[N][2];
 10 vector<int> g[N];
 11 stack<int> st;
 12 int ans,cnt;
 13 void init(int n)
 14 {
 15     ans = cnt = 0;
 16     for(int i=0; i<=n; ++i)
 17     {
 18         in[i] = 0;
 19         father[i] = i;
 20         g[i].clear();
 21     }
 22 }
 23 int topSort(int n, int m)
 24 {
 25     int i,tmp = 0,flag = 0,k = 0;
 26     for(i=0; i<n; ++i)
 27         if(father[i]==i)
 28         {
 29             cnt++;
 30             if(in[i]==0)
 31             {
 32                 st.push(i);
 33                 tmp++;
 34             }
 35             if(tmp>1)//如果一次入栈的结点大于1个,那么就说明拓扑排序不唯一
 36                 flag = 2;
 37         }
 38     while(!st.empty())
 39     {
 40         int u = st.top();
 41         st.pop();
 42         k++;
 43         tmp = 0;
 44         for(i=0; i<g[u].size(); ++i)
 45         {
 46             int v = g[u][i];
 47             in[v]--;
 48             if(in[v]==0)
 49             {
 50                 tmp++;
 51                 st.push(v);
 52             }
 53         }
 54         if(tmp>1) flag = 2;
 55     }
 56     if(k < cnt) flag = 1;//存在回路
 57     return flag;
 58 }
 59 int find(int x)
 60 {
 61     if(x == father[x])
 62         return x;
 63     return father[x] = find(father[x]);
 64 }
 65 int main()
 66 {
 67     int n,m,i,ru,rv;
 68     while(scanf("%d%d",&n,&m)!=EOF)
 69     {
 70         init(n);
 71         for(i=0; i<m; ++i)
 72         {    
 73             scanf("%d%s%d",&u[i],str[i],&v[i]);
 74             if(str[i][0]=='=')//将相等的结点并在一个集合里
 75             {
 76                 ru = find(u[i]);
 77                 rv = find(v[i]);
 78                 if(ru!=rv)
 79                     father[ru] = rv;
 80             }
 81         }
 82         for(i=0; i<n; ++i) find(i);//这样就能用father[i]得到i的父亲
 83         for(i=0; i<m; ++i)
 84         {
 85             if(father[u[i]] == father[v[i]] && str[i][0]!='=')
 86             {
 87                 ans = 1;
 88                 break;
 89             }
 90             else if(str[i][0]=='>')
 91             {
 92                 g[father[u[i]]].push_back(father[v[i]]);
 93                 in[father[v[i]]]++;
 94             }    
 95             else if(str[i][0]=='<')
 96             {
 97                 g[father[v[i]]].push_back(father[u[i]]);
 98                 in[father[u[i]]]++;
 99             }
100         }
101         if(!ans) ans = topSort(n,m);
102         if(!ans) puts("OK");
103         else if(ans==1) puts("CONFLICT");
104         else puts("UNCERTAIN");
105     }
106     return 0;
107 }
View Code

 

http://poj.org/problem?id=1094

给定n个点,m条边,

要我们逐步判断随着边的增加,能否确定唯一拓扑排序,或者是否有环。  否则就是不能确定

先判断有没有环,如果有环,输出有环

如果没有环,那么拓扑判断是否能确定唯一排序

  1 #include <stdio.h>
  2 #include <string.h>
  3 #include <vector>
  4 using namespace std;
  5 const int N = 30;
  6 int g[N][N],start,n,m,in[N],ans[N];
  7 bool vis[N],vis2[N],hasCircle,confirmSequeue;
  8 void dfs(int u)
  9 {
 10     for(int i=0; i<n; ++i)
 11     {
 12         if(g[u][i]==1)
 13         {
 14             if(start==i)
 15             {
 16                 hasCircle = true;
 17                 return;
 18             }
 19             if(!vis2[i])
 20             {
 21                 vis2[i] = true;
 22                 dfs(i);
 23             }
 24             
 25         }
 26     }
 27 }
 28 void topSort()
 29 {
 30     int i,j,k;
 31     for(i=0; i<n; ++i)
 32         for(j=0; j<n; ++j)
 33             if(g[i][j])
 34                 in[j]++;
 35     int cnt     = 0;
 36     for(j=0; j<n; ++j)
 37         if(in[j]==0)
 38             cnt++;
 39     if(cnt>1)//不能确定唯一拓扑排序
 40     {
 41         confirmSequeue = false;
 42         return;
 43     }
 44     for(i=0; i<n; ++i)
 45     {
 46         for(j=0;in[j]&&j<n;++j)
 47             NULL;
 48         in[j] = -1;
 49         ans[i] = j;
 50         cnt = 0;
 51         for(k=0; k<n; ++k)
 52         {
 53             if(g[j][k])
 54             {
 55                 in[k]--;
 56                 if(in[k]==0)
 57                 cnt++;
 58             }    
 59             if(cnt>1)
 60             {
 61                 confirmSequeue = false;
 62                 return;
 63             }    
 64         }
 65     }
 66 }
 67 int main()
 68 {
 69     int i,cnt,u,v,j;
 70     char str[5];
 71     while(true)
 72     {
 73         scanf("%d%d",&n,&m);
 74         if(n==0 && m==0) break;
 75         memset(vis,0,sizeof(vis));
 76         memset(g,0,sizeof(g));
 77         hasCircle = false;
 78         confirmSequeue = true;
 79         cnt = 0;
 80         for(i=0; i<m; ++i)
 81         {
 82             scanf("%s",str);
 83             u = str[0] - 'A';
 84             v = str[2] - 'A';
 85             g[u][v] = 1;
 86             if(!vis[u]) {vis[u] = true; cnt++;}
 87             if(!vis[v]) {vis[v] = true; cnt++;}
 88             for(j=0; j<n; ++j)//dfs判断是否有环
 89             {
 90                 memset(vis2,0,sizeof(vis2));
 91                 vis2[j] = true;
 92                 start = j;
 93                 dfs(j);
 94                 if(hasCircle) break;
 95             }
 96             if(hasCircle) break;
 97             if(cnt==n)//有n个顶点的时候才进行拓扑排序
 98             {
 99                 confirmSequeue = true;
100                 memset(in,0,sizeof(in));
101                 topSort();
102                 if(confirmSequeue)
103                     break;
104             }
105         }
106         if(cnt == n && confirmSequeue)
107         {
108             printf("Sorted sequence determined after %d relations: ",i+1);
109             for(j=0; j<n; ++j)
110                 printf("%c",ans[j]+'A');
111             puts(".");
112         }    
113         else if(hasCircle)
114             printf("Inconsistency found after %d relations.\n",i+1);
115         else
116             printf("Sorted sequence cannot be determined.\n");
117         for(i++;i<m; ++i) 
118             scanf("%s",str);
119     }
120 }
View Code

 

posted @ 2015-03-16 16:25  justPassBy  阅读(299)  评论(0编辑  收藏  举报