(intermediate) 混合图欧拉回路 UVA 10735 - Euler Circuit

Problem D
Euler Circuit
Input:
 standard input
Output: standard output
Time Limit: 2 seconds

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                               Output for 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

 

1 3 4 2 5 6 5 4 1

 

No euler circuit exist

 


Problem setter: Jimmy Mårdell, Member of Elite Problemsetters' Panel




题意:给出一个既有有向边,又有无向边的图,问有没有欧拉回路(其中无向边只能选一个方向走),有的话输出其中一条可行的路径。


思路:我们来考虑一下有向图在什么情况下会有欧拉回路。 就是每个点的入度=出度的时候。那么如果其中有些边是无向边怎么办呢?其实我们可以用最朴素的办法,枚举无向边的方向,然后判断是不是所有点是否满足条件。但是这个办法显然是指数级别的。。。 我们假设已经为每个无向边都指定了一个方向,得到了一个新的有向图,统计每个点的入度和出度,如果某一个点的入读和出度的差是奇数,那么原图一定没有欧拉回路。为什么呢?

  (之后的分析我们可以忽略原图的有向边)因为我们假设把某一条边换一个方向,那么和这条边关联的点,一定是入度+1出度-1 或者入度-1出度+1,那么不管怎么样入度和出度的差都不会是偶数,也就不可能会是0了。   那么如果所有的差为偶数呢?  如果入度-出度=x(x是偶数),假设x>0 , 那么表示我们需要调整x/2条边,使得这个点的入度==出度。因为每调整一条边,入度就-1,出度就+1,所以需要调整x/2条边,对于出度比入度大的一样的。   那么我们怎么调整呢? 这时候就需要用网络流来做了,我们的建图方式是忽略原图的有向边,你的无向边原来定了什么方向,网络流的图就用什么方向,容量为1,然后对于入度比出度大的,从源点s连一条容量为x/2的边 。出度比入度大的话,就连一条边到汇点t,容量是x/2,  注意这里的x是不一样的。  那么我们就跑一下网络流,如果满流了,说明我们能够通过调整边方向使得原图存在欧拉回路,否则不存在。  这里面网络流的作用是选择一些边后将它们反向。

如果满流了。我们只需要将那些代表无向边的有流量的边反过来,就能满足每个点的入度和出度相等了。所以 根据最后的残余网络能确定无向边的方向。最后输出的话,用什么套圈算法。。。好像好高深的样子。。。很短的。  具体看代码吧。


代码:

#include<iostream>
#include<cstdio>
#include<string.h>
#include<algorithm>
#include<queue>
#include<cmath>
#include<cstring>
#include<vector>
using namespace std;
const int maxn = 100 + 5, maxm = 500 + 5;
const int inf = 1e8;
struct Edge
{
	int u, v;
	int flow, cap;
	Edge(int u, int v, int flow, int cap)
		:u(u), v(v), flow(flow), cap(cap) { }
};

vector<Edge> edges;
vector<Edge> ori_edges;
vector<int> G[maxn];

int in[maxn], out[maxn];
int N, M;

void init()
{
	edges.clear();
	for (int i = 0; i < maxn; ++i) G[i].clear();
}

void add(int u, int v, int cap)
{
	edges.push_back(Edge(u, v, 0, cap));
	edges.push_back(Edge(v, u, 0, 0));
	int m = edges.size();
	G[u].push_back(m - 2);
	G[v].push_back(m - 1);
}

struct ISAP
{
	int d[maxn], p[maxn], num[maxn], cur[maxn];
	int n, s, t;
	void init(int n) { this->n = n; }


	int Augment()
	{
		int x = t, a = inf;
		while (x != s)
		{
			Edge&e = edges[p[x]];
			a = min(a, e.cap - e.flow);
			x = e.u;
		}
		x = t;
		while (x != s)
		{
			edges[p[x]].flow += a;
			edges[p[x] ^ 1].flow -= a;
			x = edges[p[x]].u;
		}
		return a;
	}


	void bfs()
	{
		for (int i = 0; i<n; ++i) d[i] = inf;
		queue<int> q;
		d[t] = 0;
		q.push(t);
		while (q.size())
		{
			int x = q.front(); q.pop();
			for (int i = 0; i<G[x].size(); ++i)
			{
				Edge&e = edges[G[x][i]];
				if (e.cap>0 || d[e.v] <= d[x] + 1) continue;
				d[e.v] = d[x] + 1;
				q.push(e.v);
			}
		}
	}


	int maxflow(int s, int t)
	{
		this->s = s, this->t = t;
		memset(num, 0, sizeof(num));
		memset(cur, 0, sizeof(cur));
		bfs();
		for (int i = 0; i<n; ++i)
		if (d[i] != inf) ++num[d[i]];
		int flow = 0, x = s;
		while (d[s]<n)
		{
			if (x == t)
			{
				flow += Augment();
				x = s;
			}
			int ok = 0;
			for (int i = cur[x]; i<G[x].size(); ++i)
			{
				Edge&e = edges[G[x][i]];
				if (e.cap>e.flow&&d[e.v] + 1 == d[x])
				{
					ok = 1;
					cur[x] = i;
					p[e.v] = G[x][i];
					x = e.v;
					break;
				}
			}
			if (!ok)
			{
				int m = n - 1;
				for (int i = 0; i<G[x].size(); ++i)
				{
					Edge&e = edges[G[x][i]];
					if (e.cap>e.flow) m = min(m, d[e.v]);
				}
				if (--num[d[x]] == 0) break;
				++num[d[x] = m + 1];
				cur[x] = 0;
				if (x != s) x = edges[p[x]].u;
			}
		}
		return flow;
	}
}solver;

void input()
{
	scanf("%d%d", &N, &M);
	ori_edges.clear();
	memset(in, 0, sizeof(in));
	memset(out, 0, sizeof(out));
	init();
	for (int i = 0; i < M; ++i)
	{
		int u, v; char c;
		scanf("%d %d %c", &u, &v, &c);
		if (c == 'U') add(u, v, 1);
		else ori_edges.push_back(Edge(u, v, 0, 0));
		++in[v], ++out[u];
	}
}

bool ok()
{
	for (int i = 1; i <= N;++i) 
	if ((in[i] - out[i]) % 2) return false;
	return true;
}

bool vis[maxm * 10];
void find_path(int u, vector<int>&path)
{
	for (int i = 0; i < G[u].size(); ++i)
	{
		int x = G[u][i];
		if (vis[x]) continue;
		vis[x] = true;
		Edge&e = ori_edges[x];
		find_path(e.v, path);
		path.push_back(e.v);
	}
}

void solve()
{
	if (!ok()) { printf("No euler circuit exist\n"); return; }
	solver.init(N + 2);
	int s = 0, t = N + 1;
	for (int i = 1; i <= N; ++i)
	{
		if (in[i] > out[i]) add(i, t, (in[i] - out[i]) / 2);
		else if (in[i] < out[i]) add(s, i, (out[i] - in[i]) / 2);
	}
	int sum = 0;
	for (int i = 0; i < G[s].size(); ++i)
		sum += edges[G[s][i]].cap;
	int ret = solver.maxflow(s, t);
	if (ret < sum) { printf("No euler circuit exist\n"); return; }
	for (int i = 1; i <= N; ++i)
	{
		for (int j = 0; j < G[i].size(); ++j)
		{
			Edge&e = edges[G[i][j]];
			if (e.cap == 0) continue;
			if (e.u<1 || e.u>N) continue;
			if (e.v<1 || e.v>N) continue;
			if (e.flow >= 1) ori_edges.push_back(Edge(e.v, e.u, 0, 0));
			else ori_edges.push_back(Edge(e.u, e.v, 0, 0));
		}
	}
	init();
	for (int i = 0; i < ori_edges.size(); ++i)
	{
		int u = ori_edges[i].u;
		G[u].push_back(i);
	}
	vector<int> ans;
	memset(vis, 0, sizeof(vis));
	find_path(1, ans);
	printf("1");
	for (int i = ans.size() - 1; i >= 0; --i) printf(" %d", ans[i]);
	puts("");
}

int main()
{
	int T; cin >> T;
	while (T--)
	{
		input();
		solve();
		if (T) printf("\n");
	}
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值