最小字典序欧拉路径

欧拉路就是所有边都走一次,也只走一次。
欧拉回路就是能够回到起点,欧拉路径没有这么多要求。
算法本质是这样的:
从起点开始,尽可能地不去走桥(走完之后会把图分成两半),而去走其他边,这样的输出是欧拉路径。
但是判桥的过程较为麻烦,我们可以采取这样的手段。
如果起点开始有两条边,一条边是应该走的边,另一条是桥。如果我们采用 d f s dfs dfs的方式先遍历到底直到无路可走的时候才加入答案栈中,我们容易知道的是最后无路可走的一定是最后一个点(不可能在之前输出,否则…就不存在这样的路径)每次都采用这样的手段,把无路可走的放在栈后面,有路可走的等到最后无路可走的时候再放上去。
最后即为答案。
因为常常这样的路径会导致递归栈占用过大,而且答案数组也可能会过大(放在递归里貌似占用递归栈)。
所以采用非递归的写法。
对于欧拉路径的判断呢:
首先考虑欧拉路径(两个奇点,其他都是入度出度相等)
其次考虑欧拉回路(因为可能不连通,所有入度出度都相等并且起点出度>0,可能存在多个欧拉环的情况,要特殊判边)
这是模板(已通过超坑题目验证)

#include
#define FOR(i,a,b) for(int i=a;i<=b;i++)
using namespace std;

int n,m;
int dfs_st[10000500],dfn=0;
int ans[10000500],cnt=0,num=0;

vector<int>G[1000050];
int cur[1000050];
int ind[1000050],out[1000050];

void dfs(int x){
    FOR(i,1,n)sort(G[i].begin(),G[i].end());
    dfs_st[++dfn]=x;
    memset(cur,-1,sizeof(cur));
    while(dfn>0){
        int u=dfs_st[dfn];
        int complete=1;
        for(int i=cur[u]+1;i<G[u].size();i++){
            int v=G[u][i];
            num++;
            dfs_st[++dfn]=v;
            cur[u]=i;
            complete=0;
            break;
        }
        if(complete)ans[++cnt]=u,dfn--;
    }
}

bool check(int &start){
    int l=0,r=0,mid=0;
    FOR(i,1,n){
        if(ind[i]==out[i]+1)l++;
        if(out[i]==ind[i]+1)r++,start=i;
        if(ind[i]==out[i])mid++;
    }
    if(l==1&&r==1&&mid==n-2)return true;
    l=1;
    FOR(i,1,n)if(ind[i]!=out[i])l=0;
    if(l){
        FOR(i,1,n)if(out[i]>0){
            start=i;
            break;
        }
        return true;
    }
    return false;
}

int main(){
    cin>>n>>m;
    FOR(i,1,m){
        int x,y;scanf("%d%d",&x,&y);
        G[x].push_back(y);
        ind[y]++,out[x]++;
    }
    int start=-1,ok=true;
    if(check(start)){
        dfs(start);
        if(num!=m){
            puts("What a shame!");
            return 0;
        }
        for(int i=cnt;i>=1;i--)
            printf("%d ",ans[i]);
        puts("");
    }
    else puts("What a shame!");
}

你可能感兴趣的:(队内集训,心得,欧拉路径)