原題……惋惜不會……真是一隻大蒟蒻……node
————————————————————————————————ios
有一天一位靈魂畫師畫了一張圖,如今要你找出歐拉回路,即在圖中找一個環使得每條邊都在環上出現剛好一次。算法
一共兩個子任務:數組
第一行一個整數 tt,表示子任務編號。t∈{1,2}t∈{1,2},若是 t=1t=1 則表示處理無向圖的狀況,若是 t=2t=2 則表示處理有向圖的狀況。函數
第二行兩個整數 n,mn,m,表示圖的結點數和邊數。ui
接下來 mm 行中,第 ii 行兩個整數 vi,uivi,ui,表示第 ii 條邊(從 11 開始編號)。保證 1≤vi,ui≤n1≤vi,ui≤n。atom
圖中可能有重邊也可能有自環。spa
若是不能夠一筆畫,輸出一行 「NO」。debug
不然,輸出一行 「YES」,接下來一行輸出一組方案。設計
1 3 3 1 2 2 3 1 3
YES 1 2 -3
2 5 6 2 3 2 5 3 4 1 2 4 2 5 1
YES 4 1 3 5 2 6
1≤n≤10^5,0≤m≤2×10^51≤n≤10^5,0≤m≤2×10^5
時間限制:1s1s
空間限制:256MB
http://uoj.ac/problem/117
vfk的題解太神看不懂QAQ
(這個東西真的有點像小學的一筆畫呢)
首先呢有向圖的歐拉回路就是入度與出度之和必定是一個偶數(若是入度爲1,出度爲-1,和爲0),無向圖的點度必定是偶數,用這個來判斷是否爲歐拉回路
檢查連通性能夠用並查集(《ACM國際大學生程序設計競賽算法與實現》上是這麼搞的,後來uoj的提交被我debug沒了……)
而後仔細看了看書又用gdb跑標程,總算是懂了……爲了避免讓和本身同樣的人也懵好久(由於公子是個善良的人)……因此畫了這些圖幫助理解(也幫助本身回憶www)
(哎呀混合圖的歐拉回路等學到了再說……)
傳說中的套圈法
就是搜到一個圈,再搜一個圈,圈圈相套……遞歸時應該是這樣的
咱們搜到的圈以搜到的順序存在函數堆棧裏
而後回退,直到有一個點還有沒搜到的邊,把搜到的邊存在一個數組裏
可是咱們搜到的順序很神奇是反的(由於最後函數總要回退到底端來結束一筆畫搜索)
reverse是翻轉,123456進去了就是654321
#include <cstdio> #include <cstring> #include <iostream> #define siji(i,x,y) for(int i=(x);i<=y;i++) #define gongzi(j,x,y) for(int j=(x);j>=y;j--) #define ivory(z,x) for(int z=head[x];z;z=edge[z].next) #define pii pair<int,int> #define pi acos(-1.0) #define fi first #define se second #define mod 1000007 #define inf 1<<30 #define N 100010 #define M 400100 using namespace std; int head[N],size[N],sum=1,adj[N]; struct node { int to,next; }edge[M];//偶數是正着走,奇數是反着走 inline void add(int &u,int &v) { edge[++sum].to=v; edge[sum].next=head[u]; head[u]=sum; adj[u]=head[u]; } inline int getint() { char c; while (c = getchar(), '0' > c || c > '9'); int res = c - '0'; while (c = getchar(), '0' <= c && c <= '9') res = res * 10 + c - '0'; return res; } bool used[M]; int ans[M],cur; inline int gets(int &x) { return x%2==0 ? x/2 : -x/2 ; } void dfs1(int u)//主要的程序部分 { while(adj[u]!=0) { if(!used[adj[u]]) { used[adj[u]]=1; used[adj[u]^1]=1; int k=adj[u]; dfs1(edge[k].to); ans[++cur]=gets(k); } else adj[u]=edge[adj[u]].next;//用一個額外的指針減小遍歷次數,否則會T,QAQ } } void dfs2(int u)//主要的程序部分 { while(adj[u]!=0) { if(!used[adj[u]]) { used[adj[u]]=1; int k=adj[u]; dfs2(edge[k].to); ans[++cur]=k-1; } else adj[u]=edge[adj[u]].next; } } bool solve1()//無向圖處理 { int n,m; n=getint();m=getint(); int u,v; siji(i,1,m) {u=getint();v=getint();add(u,v);add(v,u);size[u]++;size[v]++;} siji(i,1,n) { if(size[i]%2) return false; } siji(i,1,n) { if(adj[i]) {dfs1(i);break;} } if(cur*2 !=sum-1) return false;//不用並查集的話檢查獲得的邊數與邊的總數是否相等,sum的起始點是2 printf("YES\n"); return true; } bool solve2()//有向圖處理 { int n,m; n=getint();m=getint(); int u,v; siji(i,1,m) {u=getint();v=getint();add(u,v);size[u]--;size[v]++;} siji(i,1,n) { if(size[i]!=0) return false;//這個會被UOJ上一個額外數據卡 } siji(i,1,n) if(adj[i]) {dfs2(i);break;} if(cur!=sum-1) return false; printf("YES\n"); return true; } int main() { //freopen("f1.in","r",stdin); int t=getint(); if(t==1 ? solve1() : solve2() ) { gongzi(i,cur,1) { printf("%d ",ans[i]);} printf("\n"); } else printf("NO\n"); return 0; }
UOJ數據好坑
學了一個手動開大棧的方法 g++ file.cpp -o filename -g -Wl,--stack=268435456