欧拉路模板题目(uoj117)

本文介绍了一种求解欧拉回路的经典算法,并通过具体的代码实现展示了如何处理无向图和有向图的欧拉回路问题。适用于竞赛编程及图论爱好者。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

链接:http://uoj.ac/problem/117

题目描述

有一天一位灵魂画师画了一张图,现在要你找出欧拉回路,即在图中找一个环使得每条边都在环上出现恰好一次。

一共两个子任务:

  1. 这张图是无向图。(50分)
  2. 这张图是有向图。(50分)

输入格式

第一行一个整数  t t,表示子任务编号。 t{1,2} t∈{1,2},如果  t=1 t=1 则表示处理无向图的情况,如果  t=2 t=2 则表示处理有向图的情况。

第二行两个整数  n,m n,m,表示图的结点数和边数。

接下来  m m 行中,第  i i 行两个整数  vi,ui vi,ui,表示第  i i 条边(从  1 1 开始编号)。保证  1vi,uin 1≤vi,ui≤n

  1. 如果  t=1 t=1 则表示  vi vi 到  ui ui 有一条无向边。
  2. 如果  t=2 t=2 则表示  vi vi 到  ui ui 有一条有向边。

图中可能有重边也可能有自环。

输出格式

如果不可以一笔画,输出一行 “NO”。

否则,输出一行 “YES”,接下来一行输出一组方案。

  1. 如果  t=1 t=1,输出  m m 个整数  p1,p2,,pm p1,p2,…,pm。令  e=pi e=∣pi∣,那么  e e 表示经过的第  i i 条边的编号。如果  pi pi 为正数表示从  ve ve 走到  ue ue,否则表示从  ue ue 走到  ve ve
  2. 如果  t=2 t=2,输出  m m 个整数  p1,p2,,pm p1,p2,…,pm。其中  pi pi 表示经过的第  i i 条边的编号。
  3. 样例一

    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

    限制与约定

    1n105,0m2×105 1≤n≤105,0≤m≤2×105

    时间限制 1s 1s

    空间限制 256MB

    特别裸的欧拉回路,没什么好分析的,直接用套圈法解决,但是以前学习欧拉回路时候的那个博客没找到,等有时间找到在补发分析。。。
  4. 代码:
  5. #include <cstdio>
    #include <cstring>
    #define ele int
    using namespace std;
    #define maxn 100010
    #define maxm 400010
    struct edge{
    	ele v;
    	bool ban;
    	edge *nxt;
    }ep[maxm];
    ele t,n,m,ecnt,cnt,ind[maxn],outd[maxn],ans[maxm];
    edge *g[maxn];
    inline void addedge(ele u,ele v){
    	edge *p=&ep[ecnt++];
    	p->v=v; p->ban=false; p->nxt=g[u];
    	g[u]=p;
    }
    inline edge* rev(edge *j){
    	ele i=j-ep;
    	return ep+(i^1);
    }
    void dfs_ud(ele i){
    	while (g[i])
    		if (!g[i]->ban){
    			edge *j=g[i];
    			j->ban=true;
    			rev(j)->ban=true;
    			dfs_ud(j->v);
    			ele tmp=j-ep;
    			if (tmp&1)
    				tmp=-(tmp/2+1);
    			else tmp=tmp/2+1;
    			ans[cnt++]=tmp;
    		}
    		else g[i]=g[i]->nxt;
    }
    void dfs_d(ele i){
    	while (g[i])
    		if (!g[i]->ban){
    			edge *j=g[i];
    			j->ban=true;
    			dfs_d(j->v);
    			ans[cnt++]=j-ep+1;
    		}
    		else g[i]=g[i]->nxt;
    }
    int main(){
    	scanf("%d%d%d",&t,&n,&m);
    	ecnt=0;
    	memset(g,0,sizeof(g));
    	memset(ind,0,sizeof(ind));
    	memset(outd,0,sizeof(outd));
    	for (int i=0; i<m; ++i){
    		ele u,v;
    		scanf("%d%d",&u,&v);
    		--u,--v;
    		addedge(u,v);
    		++outd[u]; ++ind[v];
    		if (t==1)
    			addedge(v,u),++outd[v],++ind[u];
    	}
    	cnt=0;
    	if (t==1){
    		bool flag=true;
    		for (int i=0; i<n; ++i)
    			if (ind[i]&1){
    				flag=false;
    				break;
    			}
    		if (!flag)
    			puts("NO");
    		else{
    			for (int i=0; i<n; ++i){
    				dfs_ud(i);
    				if (cnt) break;
    			}
    			if (cnt==m){
    				puts("YES");
    				for (int i=cnt-1; i>=0; --i)
    					printf("%d ",ans[i]);
    				puts("");
    			}
    			else puts("NO");
    		}
    	}
    	else{
    		edge *p=g[0];
    		while (p)
    			p=p->nxt;
    		bool flag=true;
    		for (int i=0; i<n; ++i)
    			if (ind[i]!=outd[i]){
    				flag=false;
    				break;
    			}
    		if (!flag)
    			puts("NO");
    		else{
    			for (int i=0; i<n; ++i){
    				dfs_d(i);
    				if (cnt) break;
    			}
    			if (cnt==m){
    				puts("YES");
    				for (int i=cnt-1; i>=0; --i)
    					printf("%d ",ans[i]);
    				puts("");
    			}
    			else puts("NO");
    		}
    	}
    	return 0;
    }
    


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值