欧拉回路(模板题)

序言:

首先感谢@G20222222_tly学长提供的关于dfs的思路一份。

在此之前,如果看过我之前写的博客的同学,不必担心,只需要,忘记!

引子:

额,这道题,以平常的题目,过人的惊天数据来展示什么叫毒瘤

然后去看了一下题解, 自己又重码了一遍,结果重新再来看的时候是一脸懵逼嗄。

然后想了将近一晚上的思路终于有了起色。

下面就是我的一些见解,和代码


代码及其思路

首先加入快读,是因为原代码是刚好卡着时间过的,

如果要按照,本人目前所学的东西来写的话

直接超时,不用说了。

关于dfs最新的思路就是这样的:

就是退回来的时候的边塞进ans里面,意思就是:

我们首先考虑就是无向边会拆分成正反两条边,

j j j条边,正向就是 > > 1 >> 1 >>1,反向就是 j < < 1 ∣ 1 j << 1 | 1 j<<11

若要在正反两条边上同时打一个 v i s vis vis标记,就打在 j > > 1 j >> 1 j>>1上。

~~ 以上来自tly学长思路 ~~

之后就是判断是否是有向图或无向图, 如果都不是那就没有欧拉回路,而且如果图不连通也是不存在的

最后就是将思路里讲的处理负数


/* 
 当图是无向图时,欧拉回路的存在条件为所有点的入度为偶数

当图是有向图时,欧拉回路的存在条件是所有点的入度等于出度

求欧拉回路时dfs所有边 如果这条边当前没被标记过

那就标记这条边,并且继续向下深搜,把经过的边存进数组里 

同时要求实现有向图和无向图,无向图就是多建了一些边

回溯时存下所有边

然后在返回输出的时候处理负数

并且倒叙输出
*/ 
#include<cstdio>
#define maxn 100005
#define maxm 200005
using namespace std;
int last[maxn];//储存链式前向星 
int ecnt = 1 ;//边数
int cnt; //奇度顶点个数的计数器 
int ans[maxm];//储存欧拉回路 
int in_deg[maxn], out_deg[maxn];//入度 ,出度
bool vis[maxm];//记录有无关联边 

int read() {
    
    
    int x = 0, f = 1;
    char c = getchar();
    while (c < '0' || c > '9') {
    
    
        if (c == '-')
            f = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9') {
    
    
        x = (x << 3) + (x << 1) + (c ^ 48);
        c = getchar();
    }
    return x * f;
}

struct node{
    
    
	int next, to;
}e[maxm << 1];

void add(int a, int b){
    
    //点与点之间有无关联 
    e[++ecnt] = (node){
    
    last[a], b};
    last[a] = ecnt;
}

void dfs(int x){
    
    
    for(int &i = last[x]; i; i = e[i].next){
    
    
        int y = e[i].to, j = i;
        if(!vis[j >> 1]){
    
    
            vis[j >> 1] = 1;
            dfs(y);
            ans[++cnt] = j;
        } 
    }
}

int main(){
    
    
    int t, n, m, a, b;
    t = read();
    n = read();
    m = read();
    for(int i = 1; i <= m; i++){
    
    
        a = read();
        b = read();
        add(a , b);
        if(t == 1)add(b , a), in_deg[a]++, in_deg[b]++;//无向图 出入度加一 
        else ecnt++, in_deg[b]++, out_deg[a]++;
    }

    if(t == 1){
    
    //有一条无向边
        for(int i = 1; i <= n; i++)
            if((in_deg[i] + out_deg[i]) & 1){
    
    //如果某点的度是奇数
            	printf("NO\n");
            	return 0;
			}
    }
    else {
    
    //有一条有向边 
        for(int i = 1; i <= n; i++)
            if(in_deg[i] != out_deg[i]){
    
    
            	printf("NO\n");
            	return 0;
			}
    }   
    dfs(a);
    if(cnt != m){
    
    //如果图并不是联通的 
        puts("NO");
    } 
    else{
    
    //相反操作
        puts("YES");
        for(int i = cnt; i; i--){
    
    
            printf("%d ",ans[i]&1?-(ans[i]>>1):(ans[i]>>1));
        }
    }
} 

这个注释和思路,大概率会有错,如有问题,请指出!

猜你喜欢

转载自blog.csdn.net/C202207LYX/article/details/108597735