暑假集訓2016day3T1 歐拉回路(UOJ #117歐拉回路)(史上最全的歐拉回路純無向圖/有向圖解析)

原題……惋惜不會……真是一隻大蒟蒻……node

————————————————————————————————ios

有一天一位靈魂畫師畫了一張圖,如今要你找出歐拉回路,即在圖中找一個環使得每條邊都在環上出現剛好一次。算法

一共兩個子任務:數組

  1. 這張圖是無向圖。(50分)
  2. 這張圖是有向圖。(50分)

輸入格式

第一行一個整數 tt,表示子任務編號。t{1,2}t∈{1,2},若是 t=1t=1 則表示處理無向圖的狀況,若是 t=2t=2 則表示處理有向圖的狀況。函數

第二行兩個整數 n,mn,m,表示圖的結點數和邊數。ui

接下來 mm 行中,第 ii 行兩個整數 vi,uivi,ui,表示第 ii 條邊(從 11 開始編號)。保證 1vi,uin1≤vi,ui≤n。atom

  1. 若是 t=1t=1 則表示 vivi 到 uiui 有一條無向邊。
  2. 若是 t=2t=2 則表示 vivi 到 uiui 有一條有向邊。

圖中可能有重邊也可能有自環。spa

輸出格式

若是不能夠一筆畫,輸出一行 「NO」。debug

不然,輸出一行 「YES」,接下來一行輸出一組方案。設計

  1. 若是 t=1t=1,輸出 mm 個整數 p1,p2,,pmp1,p2,…,pm。令 e=pie=∣pi∣,那麼 ee 表示通過的第 ii 條邊的編號。若是 pipi 爲正數表示從 veve 走到 ueue,不然表示從 ueue 走到 veve。
  2. 若是 t=2t=2,輸出 mm 個整數 p1,p2,,pmp1,p2,…,pm。其中 pipi 表示通過的第 ii 條邊的編號。

樣例一

input

1
3 3
1 2
2 3
1 3

output

YES
1 2 -3

樣例二

input

2
5 6
2 3
2 5
3 4
1 2
4 2
5 1

output

YES
4 1 3 5 2 6

限制與約定

1n10^5,0m2×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

相關文章
相關標籤/搜索