UVa 10735 Euler Circuit

本文介绍了一种结合最大流算法寻找特殊图中欧拉回路的方法。该图包含有向边和无向边,通过巧妙地将无向边转换为有向边,并利用最大流判断是否存在欧拉回路。若存在,则进一步通过算法构造具体的欧拉回路路径。
An Euler circuit is a graph traversal starting and ending at the same vertex and using every edge
exactly once. Finding an Euler circuit in an undirected or directed graph is a fairly easy task, but what
about graphs where some of the edges are directed and some undirected? An undirected edge can only
be traveled in one direction. However, sometimes any choice of direction for the undirected edges may
not yield an Euler circuit.
Given such a graph, determine whether an Euler circuit exists. If so, output such a circuit in the
format specified below. You can assume that the underlying undirected graph is connected.
Input
The first line in the input contains the number of test cases, at most 20. Each test case starts with a
line containing two numbers, V and E the number of vertices (1 ≤ V ≤ 100) and edges (1 ≤ E ≤ 500)
in the graph. The vertices are numbered from 1 to V . Then follows E lines specifying the edges. Each
such line will be in the format a b type where a and b are two integers specifying the endpoints of the
edge. type will either be the character ‘U’, if the edge is undirected, or ‘D’, if the edge is directed. In
the latter case, the edge starts at a and ends at b.
Output
If an Euler circuit exist, output an order in which the vertices can be traversed on a single line. The
vertex numbers should be delimited with a single space, and the start and end vertex should be included
both at the beginning and the end of the sequence. Since most graphs have multiple solutions, any valid
solution will be accepted. If no solution exist, output the line ‘No euler circuit exist’. Output a
blank line between each test case.
Sample Input
2
6 8
1 3 U
1 4 U
2 4 U
2 5 D
3 4 D
4 5 U
5 6 D
5 6 U
4 4
1 2 D
1 4 D
2 3 U
3 4 U
Sample Output
1 3 4 2 5 6 5 4 1

No euler circuit exist

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

最大流+欧拉回路~

……这题太强了,比我以前做的那些不知高到哪里去了……

因为无向边只能选一个方向来走,所以不能两个方向都建边,所以就把有向边和无向边分别建成两个图,无向边图每条边都任选一个方向,记录。然后在无向边建成的有向图上跑最大流,如果最大流满流,则有欧拉回路,把无向边按照方向和流量正负加入有向边图中,再跑一遍欧拉回路即可。

中间还有很多处理,特别麻烦啊,改了一下午……

(建议建图不要像我这样用fi和fi1之类的来分,超容易混,我查错的时候就是改这个改到吐血……)

(数组开大!数组开大!数组开大!)


#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<stack>
#include<queue>
using namespace std;

int t,n,m,x,y,fi[201],w[12100],ne[12100],cnt,dis[201],tot,ans,now;
int fi1[201],w1[12100],ne1[12100],v1[12100],cnt1,ru[201],totnum,ho[12100];
char s[3],flag;
bool b[12100],b1[12100];
stack<int> sta;

void add(int u,int v)
{
	w[++cnt]=v;ne[cnt]=fi[u];fi[u]=cnt;b[cnt]=0;
}

void add1(int u,int vv,int val)
{
	w1[++cnt1]=vv;ne1[cnt1]=fi1[u];fi1[u]=cnt1;v1[cnt1]=val;b1[cnt1]=1;ho[cnt1]=u;
	w1[++cnt1]=u;ne1[cnt1]=fi1[vv];fi1[vv]=cnt1;v1[cnt1]=0;b1[cnt1]=0;ho[cnt1]=vv;
}

bool bfs()
{
	memset(dis,0,sizeof(dis));
	queue<int> q;q.push(0);dis[0]=1;
	while(!q.empty())
	{
		int k=q.front();q.pop();
		for(int i=fi1[k];i;i=ne1[i])
		  if(!dis[w1[i]] && v1[i]>0)
		  {
		  	dis[w1[i]]=dis[k]+1;q.push(w1[i]);
		  	if(w1[i]==tot) return 1;
		  }
	}
	return 0;  
}

int findd(int u,int vv)
{
	if(u==tot || !vv) return vv;
	for(int i=fi1[u],kkz;i;i=ne1[i])
	  if(dis[w1[i]]==dis[u]+1 && v1[i] && (kkz=findd(w1[i],min(vv,v1[i]))))
	  {
	  	v1[i]-=kkz;v1[i^1]+=kkz;return kkz;
	  }
	return 0;
}

void dfs(int u)
{
	for(int i=fi[u];i;i=ne[i])
	  if(!b[i])
	  {
	  	b[i]=1;dfs(w[i]);sta.push(u);
	  }
}

int main()
{
	scanf("%d",&t);
	while(t--)
	{
		memset(fi,0,sizeof(fi));
		memset(fi1,0,sizeof(fi1));
		memset(ru,0,sizeof(ru));
		scanf("%d%d",&n,&m);cnt=cnt1=1;totnum=ans=flag=0;tot=n+1;
		for(int i=1;i<=m;i++)
		{
			scanf("%d%d%s",&x,&y,s);
			ru[x]--;ru[y]++;
			if(s[0]=='U') add1(x,y,1);
			else add(x,y);
		}
		for(int i=1;i<=n;i++)
		  if(ru[i])
		  {
		  	if(ru[i]%2)
		  	{
		  		printf("No euler circuit exist\n");flag=1;
		  		if(t) printf("\n");break;
			}
		  	if(ru[i]<0) add1(0,i,-ru[i]/2),totnum-=ru[i]/2;
		  	else add1(i,tot,ru[i]/2);
		  }
		if(flag) continue;
		while(bfs()) while(now=findd(0,999999999)) ans+=now;
		if(ans!=totnum) printf("No euler circuit exist\n");
		else
		{
			for(int i=1;i<=cnt1;i++)
			  if(b1[i] && w1[i] && w1[i]!=tot && ho[i] && ho[i]!=tot)
			  {
			  	if(v1[i]>0) add(ho[i],w1[i]);
			  	else add(w1[i],ho[i]);
			  }
			dfs(1);
			while(!sta.empty()) printf("%d ",sta.top()),sta.pop();printf("1\n");
		}
		if(t) printf("\n");
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值