CF36E Two Paths (欧拉回路+构造)

◇◆丶佛笑我妖孽 提交于 2020-05-01 09:34:24

题面传送门

题目大意:给你一张可能有重边的不保证联通的无向图,现在要在这个图上找出两条路径,恰好能覆盖所有边一次,根据边的编号输出方案,无解输出-1

 

一道很不错的欧拉路径变形题

 

首先要知道关于欧拉路径的一种算法:Hierholzer算法

这位神犇的讲解非常不错

欧拉路径与欧拉回路

我们称度为奇数的点为奇点,度为偶数的点为偶点

从一个点开始走,把其它所有边都走了一遍,叫欧拉路径

从一个点开始走,把其它所有边都走了一遍又回到了这个点,叫欧拉回路

如果图中存在欧拉回路,所有点均为偶点,画画图就明白了

如果图中不存在或仅存在两个奇点,那么这个图存在欧拉路径,且路径的起点终点一定分别是这两个奇点。把起点终点连起来不就变成欧拉回路了么

欧拉回路一定是欧拉路径

Hierholzer算法

从图中的一个奇点开始dfs,每遍历到一条边,就在图中删去这条边(包括反向边),然后递归指向的节点

直到当前节点相连的所有边都被删掉之后,把当前节点推入一个栈中,回溯

如果原图存在欧拉回路,就能搜出欧拉回路。如果存在欧拉路径,就会搜出欧拉路径。

栈中存储的是路径的倒序点序列,而边序列就是每次递归前删掉的边构成的序列

实现比较简单

 

那这道题该怎么搞呢?

(1)如果图中有>2个连通块,一定无解

(2)如果只有1个连通块,分为0个奇点,2个奇点,4个奇点讨论,其它情况都是无解

0个奇点就是欧拉回路,断开其中任意一条边,把路径拆成两条就是答案

2个奇点就是欧拉路径,断开其中任意一条边,把路径拆成两条就是答案

4个奇点的话,挑两个奇点连起来,再跑欧拉路径就行啦

(3)如果有2个连通块,说明这两条路径分别在这两个连通块里

对于每个连通块而言,只能存在0个奇点和2个奇点两种情况,讨论一下就好啦

 

代码写得好丑啊TvT

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 #define ll long long
  5 #define N1 20010 
  6 using namespace std;
  7 
  8 template <typename _T> void read(_T &ret)
  9 {
 10     ret=0; _T fh=1; char c=getchar();
 11     while(c<'0'||c>'9'){ if(c=='-') fh=-1; c=getchar(); }
 12     while(c>='0'&&c<='9'){ ret=ret*10+c-'0'; c=getchar(); }
 13     ret=ret*fh;
 14 }
 15 
 16 struct Edge{
 17 int to[N1*2],nxt[N1*2],del[N1*2],head[N1],cte;
 18 void ae(int u,int v)
 19 { cte++; to[cte]=v; nxt[cte]=head[u]; head[u]=cte; }
 20 }e;
 21 
 22 int n,m,num;
 23 int inc[N1],vis[N1],use[N1],stk[N1],tp;
 24 void dfs1(int x,int id)
 25 {
 26     int j,v; vis[x]=id; num++;
 27     for(j=e.head[x];j;j=e.nxt[j])
 28     {
 29         v=e.to[j];
 30         if(!vis[v]) dfs1(v,id);
 31     } 
 32 }
 33 void euler(int x)
 34 {
 35     int j,v,la=0;
 36     for(j=e.head[x];j;j=e.nxt[j])
 37     {
 38         if(e.del[j]) continue;
 39         v=e.to[j]; e.del[j]=1; e.del[j^1]=1;
 40         euler(v); stk[++tp]=j>>1; 
 41     } 
 42 }
 43 void fkdown(){ puts("-1"); exit(0); }
 44 int odd[N1],cnt_odd;
 45 
 46 void solve0(int id,int esum)
 47 {
 48     int i;
 49     for(i=1;i<=n;i++) if(vis[i]==id) 
 50     {
 51         euler(i); 
 52         if(tp<esum) fkdown();
 53         printf("%d\n",tp);
 54         while(tp) printf("%d ",stk[tp--]);
 55         puts(""); 
 56         break;
 57     }
 58 }
 59 void solve2(int id,int esum)
 60 {
 61     euler(odd[1]);
 62     if(tp<esum) fkdown(); // 
 63     printf("%d\n",tp);
 64     while(tp) printf("%d ",stk[tp--]);
 65     puts(""); 
 66 }
 67 
 68 int main()
 69 {
 70     freopen("input.txt","r",stdin); 
 71     freopen("output.txt","w",stdout);
 72     scanf("%d",&m);
 73     int i,j,x,y,cnt_compo=0; n=10000; e.cte=1;
 74     if(m==1) fkdown(); 
 75     for(i=1;i<=m;i++) 
 76     {
 77         read(x), read(y), e.ae(x,y), e.ae(y,x);
 78         inc[x]++, inc[y]++, use[x]=1, use[y]=1;
 79     }
 80     for(i=1;i<=n;i++) if(use[i] && !vis[i]) cnt_compo++, dfs1(i,cnt_compo);
 81     if(cnt_compo>2) fkdown();
 82     if(cnt_compo==1){
 83         for(i=1;i<=n;i++) if(inc[i]&1) odd[++cnt_odd]=i;
 84         if(!cnt_odd){
 85         
 86         for(i=1;i<=n;i++) if(inc[i]) 
 87         {
 88             euler(i); 
 89             if(tp<m) fkdown();
 90             printf("%d\n",tp-1);
 91             while(tp>1) printf("%d ",stk[tp--]);
 92             puts(""); 
 93             puts("1");
 94             while(tp>0) printf("%d ",stk[tp--]);
 95             puts(""); 
 96             break;
 97         }
 98         
 99         }else if(cnt_odd==2){
100         
101         euler(odd[1]);
102         if(tp<m) fkdown(); 
103         printf("%d\n",tp-1);
104         while(tp>1) printf("%d ",stk[tp--]);
105         puts(""); 
106         puts("1");
107         while(tp>0) printf("%d ",stk[tp--]);
108         puts(""); 
109         
110         }else if(cnt_odd==4){
111         
112         e.ae(odd[2],odd[3]); e.ae(odd[3],odd[2]);
113         euler(odd[1]);
114         if(tp-1<m) fkdown(); 
115         while(tp) 
116         {
117             if(stk[tp]>m) break; 
118             tp--;
119         }
120         printf("%d\n",m+1-tp);
121         for(i=m+1;i>tp;i--) printf("%d ",stk[i]);
122         puts(""); 
123         tp--; printf("%d\n",tp);
124         while(tp) printf("%d ",stk[tp--]);
125         puts(""); 
126             
127         }else fkdown();
128     }else{
129         int esum=0;
130         
131         cnt_odd=0; esum=0;
132         for(i=1;i<=n;i++) 
133         {
134             if(!vis[i] || vis[i]==1) continue; 
135             if((inc[i]&1)) odd[++cnt_odd]=i;
136             esum+=inc[i];
137         }
138         if(cnt_odd>2 || cnt_odd&1) fkdown();
139         
140         cnt_odd=0; esum=0;
141         for(i=1;i<=n;i++) 
142         {
143             if(!vis[i] || vis[i]==2) continue; 
144             if((inc[i]&1)) odd[++cnt_odd]=i;
145             esum+=inc[i];
146         }
147         if(cnt_odd>2 || cnt_odd&1) fkdown();
148         esum>>=1;
149         if(!cnt_odd) solve0(1,esum);
150         else if(cnt_odd==2) solve2(1,esum);
151         else fkdown();
152         
153         cnt_odd=0; esum=0;
154         for(i=1;i<=n;i++) 
155         {
156             if(!vis[i] || vis[i]==1) continue; 
157             if((inc[i]&1)) odd[++cnt_odd]=i;
158             esum+=inc[i];
159         }
160         esum>>=1;
161         if(!cnt_odd) solve0(2,esum);
162         else if(cnt_odd==2) solve2(2,esum);
163         else fkdown();
164     }
165     return 0;
166 }

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!