欧拉回路 (删边)

题目描述

原题来自:UOJ #117

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

一共两个子任务:

  1. 这张图是无向图。(5050 分)

  2. 这张图是有向图。(5050 分)

输入格式

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

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

接下来 mm 行中,第 ii 行两个整数 vi,uivi,ui,表示第 ii 条边(从 11 开始编号)。保证 1≤vi,ui≤n1≤vi,ui≤n。

  1. 如果 t=1t=1 则表示 vivi 到 uiui 有一条无向边。

  2. 如果 t=2t=2 则表示 vivi 到 uiui 有一条有向边。

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

输出格式

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

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

  1. 如果 t=1t=1,输出 mm 个整数 p1,p2,…,pmp1,p2,…,pm。令 e=|pi|e=|pi|,那么 ee 表示经过的第 ii 条边的编号。如果 pipi为正数表示从 veve 走到 ueue,否则表示从 ueue 走到 veve。

  2. 如果 t=2t=2,输出 mm 个整数 p1,p2,…,pmp1,p2,…,pm。其中 pipi 表示经过的第 ii 条边的编号。

样例 1

InputOutput
1
3 3
1 2
2 3
1 3
YES
1 2 -3

样例 2

InputOutput
2
5 6
2 3
2 5
3 4
1 2
4 2
5 1
YES
4 1 3 5 2 6

数据范围与提示

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

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stack>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
vector<int > vs;
int k=0,op,n,m,out[110100],in[1100001],first[1101000],next1[1101000],u[1100100],v[1100100],book[1100109];
void add(int x,int y)
{
	u[k]=x,v[k]=y;
	next1[k]=first[x],first[x]=k++;
}
void dfs(int x)
{
	int v1,t;
	for(int &i=first[x]; i!=-1;)/*i起着一个遍历和删边两个作用*/
	{

		if(book[i])/*x这个点是可以重复遍历的(因为欧拉路径只要求边不重复)
		因此上次标记过的边再去遍历太浪费时间了*/
		{
			i=next1[i];
			/*j代表的是上一条边的下标(&i=next[j]是本条边)因为i直接获取了next[j]的地址,
			i=next[i]相当于直接让next[j]=next[next[j]],意思就是直接跳过本条边的遍历
			相当于直接删除本边*/
			continue;
		}
		book[i]=1;
		if(op==1)
		{

			book[i^1]=1;/*标记同一个无向边*/
			/*
			规律:0^1=1 ;	0 1是一对互相反向的边
			      1^1=0;

			      2^1=3;    2 3是一对互相反向的边
			      3^1=2;

			      4^1=5;    4 5是一对互相反向的边
			      5^1=4;
			      …………

			      无向图中通过0可以找到反向边1; 前提边的下标只能从0开始存
			*/
			t=i/2+1;
			/*
			无向图中:
			0 1是一对互相反向的边 0/2=1/2=0 ;
			2 3是一对互相反向的边 2/2=3/2=1;
			4 5是一对互相反向的边 4/2=5/2=2;
			………………………………………………
			t=i/2+1 表示是第t条无向边
			*/
			if(i%2) t=-t;
		}
		else
			t=i+1;
		v1=v[i];
		i=next1[i];
		dfs(v1);
		vs.push_back(t);
	}
}
int pan()
{
	int i,j;
	for(i=1; i<=n; i++)
	{
		if(op==1&&(in[i]+out[i])%2) return 0;
		if(op==2&&in[i]!=out[i]) return 0;
	}
	for(i=1; i<=n; i++)
	{
		if(first[i]!=-1)
		{
			/*找到一个含边的点,有的点没边完全可以不遍历(欧拉路径只需要遍历所有边)*/

			dfs(i);
			break;
		}
	}
	if(vs.size()<m) return 0;/*检查是否遍历到所有边*/
	return 1;
}
int main()
{
	int i,j,a1,b1;
	scanf("%d%d%d",&op,&n,&m);
	memset(first,-1,sizeof(first));
	k=0;
	vs.clear();
	for(i=1; i<=m; i++)
	{
		scanf("%d%d",&a1,&b1);
		add(a1,b1);
		if(op==1) add(b1,a1);
		out[a1]++,in[b1]++;
	}
	if(!pan())
	{
		printf("NO\n");
		return 0;
	}
	else
	{
		printf("YES\n");
		for(i=vs.size()-1; i>=0; i--)
		{
			printf("%d%c",vs[i],i==0?'\n':' ');
		}
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值