拓扑排序入门详解&&Educational Codeforces Round 72 (Rated for Div. 2)-----D

https://codeforces.com/contest/1217

D:给定一个有向图,给图染色,使图中的环不只由一种颜色构成,输出每一条边的颜色

不成环的边全部用1染色

ps:最后输出需要注意,一个环上的序号必然是非全递增的,若有环且有一条边u->v,u的序号

 

可以用dfs染色或者用拓扑排序做

 

顺便复习一下拓扑排序:

拓扑排序是将有向无环图的所有顶点排成一个线性序列,使得图中任意两个顶点u,v若存在u->v,那么序列中u一定在v前面。

了解一个概念: DAG-->有向无环图,一个有向图的任意顶点都无法通过一些有向边回到自身,称之为DAG

 

算法过程:

(1)定义一个队列,把所有入度为0的结点加入队列(图有n个点)

(2)取队首节点,输出,删除所有从他出发的边,并令这些边的入度-1,若某个顶点的入度减为0,则将其加入队列

(3)反复进行(2)操作,直到队列为空;

注意:若队列为空时入过队的节点数目恰好为n,说明拓扑排序成功,图为DAG,否则图中有环

 

这位博主写的挺好的 https://blog.csdn.net/qq_41713256/article/details/80805338

拓扑排序入门详解&&Educational Codeforces Round 72 (Rated for Div. 2)-----D_第1张图片

 

 

拓扑排序的博客

https://www.jianshu.com/p/3347f54a3187

dfs版拓扑排序

https://blog.csdn.net/wjh2622075127/article/details/82712940

 

 1 #include
 2 
 3 using namespace std;
 4 inline int read(){
 5     int X=0,w=0;char ch=0;
 6     while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
 7     while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
 8     return w?-X:X;
 9 }
10 /*-------------------------------------------------------------------*/
11 typedef long long ll;
12 const int maxn=5010;
13 int du[maxn];
14 ll cnt;
15 int n,m;
16 int s;
17 vector<int>G[maxn];
18 pair<int,int>ans[maxn];
19 queue<int>q;
20 int main()
21 {    
22     ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
23     
24     //一个环上的序号必然是非全递增的 
25     cin>>n>>m;
26     for(int i=1;i<=m;i++){
27         
28         int u,v;
29         cin>>u>>v;
30         
31         G[u].push_back(v);
32         
33         ans[i].first=u;ans[i].second=v;
34         
35         //入度 
36         du[v]++;
37     }
38     
39     for(int i=1;i<=n;i++) if(du[i]==0) q.push(i);
40     
41     while(!q.empty()){
42         
43         int now=q.front();q.pop();
44         
45         int len=G[now].size();
46         
47         for(int i=0;i){
48             
49             du[G[now][i]]--;//该点入度-1 
50             
51             if(du[G[now][i]]==0)q.push(G[now][i]);
52             
53         }        
54     }
55     int flag=0;
56     
57     for(int i=1;i<=n;i++) 
58     if(du[i]!=0){
59         
60         flag=1;//标记是否还存在入度不为0的点 
61         break; 
62     }
63     //一个环上的序号必然是非全递增的 
64     
65     if(flag){//说明有环
66         cout<<2<<endl;
67         //for(int i=1;i<=m;i++)cout<<1<<" ";
68         for(int i=1;i<=m;i++){
69             if(ans[i].first>ans[i].second)
70             cout<<1<<" ";
71             else cout<<2<<" ";
72         }
73     }
74     else{ //无环直接输出1
75         
76         cout<<1<<endl;
77         for(int i=1;i<=m;i++)
78         cout<<1<<" ";
79     } 
80     
81     return 0;
82 }

 

 

DFS版:

dfs过程中有3个状态1,-1,0,1表示当前搜索路径,-1表示已搜索过且无环路径,0表示还未搜索,可以用前向星存边或者vector存边

其他需要注意的代码有注释

ps:讲个大家不容易理解的地方,这个DFS的点是不是可以从任意起点搜索?答案:是的,这个对拓扑序列没有影响,可通过代码自由验证。

有向图DFS过程中(不判环的情况下),我们用栈去存储他的拓扑序列,当一个点没有后驱节点时,这个节点入栈,记住栈的性质(后进先出),然后回溯,这样,越是后面的节点就会被压进栈底

比如说有向边u->v,u是v的前驱,若存在u->v>t,t在拓扑序列中一定在u的后面(拓扑排序的性质),我们从v开始搜索,到t终止(无后驱节点),回溯,入栈,v入栈,回溯。

最后我们搜索u,发现u的后驱节点已标记,所以入栈,退出完成拓扑排序

所以,以DFS回溯+栈的形式就可以很好地完成一次拓扑排序

 

前向星版本:

 1 #include
 2 
 3 using namespace std;
 4 inline int read(){
 5     int X=0,w=0;char ch=0;
 6     while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
 7     while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
 8     return w?-X:X;
 9 }
10 /*-------------------------------------------------------------------*/
11 typedef long long ll;
12 const int maxn=5010;
13 struct node{
14     int to,next;
15 }star[6000];
16 
17 ll cnt;
18 int n,m;
19 int vis[maxn],head[maxn];
20 void add(int u,int v){
21     star[cnt].to=v;
22     star[cnt].next=head[u];
23     head[u]=cnt++;
24 }
25 bool dfs(int idx){
26     
27     vis[idx]=1;
28     for(int i=head[idx];i!=-1;i=star[i].next){
29         
30         int v=star[i].to;
31         if(vis[v]==1)return false;
32         //若搜索过程中发现回到本次搜索过的点,说明有环,退出 
33         if(vis[v]==0&&!dfs(v))return false;
34         
35     }
36     vis[idx]=-1;//目前路径上不存在环,所以标记为-1 
37     return true;
38 }
39 pair<int,int >p[maxn];
40 int main()
41 {    
42     ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
43     
44     //一个环上的序号必然是非全递增的 
45     memset(head,-1,sizeof(head));
46     cin>>n>>m;
47     for(int i=1;i<=m;i++){
48         
49         int u,v;
50         cin>>u>>v;
51         add(u,v);
52         p[i].first=u,p[i].second=v;
53     }
54     int flag=0;
55     for(int i=1;i<=n;++i){
56         if(!vis[i]){
57             if(!dfs(i)){
58                 flag=1;
59                 break;
60             }
61             
62         }
63     }
64     if(flag){
65         cout<<2<<endl;
66         for(int i=1;i<=m;i++){
67             if(p[i].first>p[i].second)cout<<2<<" ";
68             else cout<<1<<" ";
69         }
70     }
71     else{
72         cout<<1<<endl;
73         for(int i=1;i<=m;i++){
74             cout<<1<<" ";
75         }
76     }
77     return 0;
78 }

 

 

vector版本:

 1 #include
 2 
 3 using namespace std;
 4 inline int read(){
 5     int X=0,w=0;char ch=0;
 6     while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
 7     while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
 8     return w?-X:X;
 9 }
10 /*-------------------------------------------------------------------*/
11 typedef long long ll;
12 const int maxn=5010;
13 struct node{
14     int to,next;
15 }star[6000];
16 
17 vector<int>edge[maxn]; 
18 
19 
20 ll cnt;
21 int n,m;
22 int vis[maxn],head[maxn];
23 
24 bool dfs(int idx){
25     
26     int len=edge[idx].size();
27     vis[idx]=1;
28     for(int i=0;ii){
29         
30         if(vis[edge[idx][i]]==1)return 0;
31         if(!vis[edge[idx][i]]&&!dfs(edge[idx][i]))return 0;
32     }
33     vis[idx]=-1;
34     return 1;
35 }
36 pair<int,int >p[maxn];
37 int main()
38 {    
39     ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
40     
41     //一个环上的序号必然是非全递增的 
42     memset(head,-1,sizeof(head));
43     cin>>n>>m;
44     for(int i=1;i<=m;i++){
45         
46         int u,v;
47         cin>>u>>v;
48         
49         p[i].first=u,p[i].second=v;
50         
51         edge[u].push_back(v);
52         
53     }
54     int flag=0;
55     
56     for(int i=1;i<=n;++i){
57         
58         if(!vis[i]){
59             if(!dfs(i)){
60                 flag=1;
61                 break;
62             }
63         }
64         
65         
66     }
67     
68     
69     if(flag){
70         cout<<2<<endl;
71         for(int i=1;i<=m;i++){
72             if(p[i].first>p[i].second)cout<<2<<" ";
73             else cout<<1<<" ";
74         }
75     }
76     else{
77         cout<<1<<endl;
78         for(int i=1;i<=m;i++){
79             cout<<1<<" ";
80         }
81     }
82     return 0;
83 }

 

 

你可能感兴趣的:(拓扑排序入门详解&&Educational Codeforces Round 72 (Rated for Div. 2)-----D)