UVA 1073 Euler Circuit

本文介绍了一种结合欧拉路径与网络流算法解决特定图论问题的方法。通过构造有向图并运用网络流算法判断是否存在欧拉路径,最后递归地找出并打印路径。代码实现了完整的算法流程。

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

有一定难度的一道题目,做之前要把思路捋一下,首先对于输入的信息,如果输入的是无向边,那么就按照输入的方式将该无向边进行定向,并且存入到一个集合A当中,为后面使用网络流算法进行铺垫,如果输入的是有向边,那么就将该条边的信息存入到另外的一个集合B当中,在存入边的信息的时候要记得对每一个节点进行相应的入度以及出度的计算,然后开始第一轮判断,首先判断每个节点的入度与出度之差的绝对值是否是2的倍数,这也就意味着,如果是2的倍数,那么就存在着一种可能,将出度或者入度增加或者调整为差值的一半,出度减少入度就会增加,反之亦然。如果不是2的倍数,那么第一轮就可以直接否定。紧接着开始进行第二轮的判断,设置一个起始点和一个终止点,假设起始点是要将所有多余的出度送出,终止点是要接收所有多余的出度,反之也可以,同时遍历所有的已知点,如果该点的出度大于入度,那么就建立起始点到该点的一条边,同时边的容量为之前提及到的差值的一半,同时如果该点的入度大于出度,那么就建立一条从该点到终止点的一条边,边的容量也是差值的一半。在之前的建立边的过程当中所有的无向边的容量都定为1。然后开始遍历从起始点开始的每一条边的容量,并且求和,记录。也就是要从起始点送出去的多余的出度的量。然后开始利用网络流算法,判断网络中的流的总流量是否和之前求出的出度之和相等,如果相等,说明可以将所有的多余的出度送出去,如果不等,就直接结束。接着,进入到关键的一步了,对于无向边,假设两个点分别为u和v,如果该边上的流量大于等于1,说明u的出度太多了,要送出一部分给v,所以在还原到原来的图形当中的时候要建立的有向边的方向应该是从v到u的,这一点很重要,我之前就在这里栽了好几个跟头。然后原图全部还原好之后,就可以递归找出欧拉路径并且打印了,这个不难。具体实现见如下代码:

#include<iostream>
#include<vector>
#include<string>
#include<set>
#include<stack>
#include<queue>
#include<map>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<cstring>
#include<sstream>
#include<cstdio>
#include<deque>
#include<functional>
using namespace std;

class Edge{
public:
	int from, to, cap, flow;
	Edge(int fr, int t, int c, int fl) :from(fr), to(t), cap(c), flow(fl){}
};

const int Inf = 0x3f3f3f3f;

class Solve{
public:
	int n, m;//n个点  m条边
	vector<Edge> edge;
	vector<Edge> oriEdge;
	vector<int> G[5050];
	int in[5050],out[5050];
	int visit[5050];
	void addEdge(vector<Edge>& edge1,int from,int to,int cap,int flow){
		edge1.push_back(Edge(from,to,cap,0));
		edge1.push_back(Edge(to, from, 0, 0));
		int m = edge1.size();
		G[from].push_back(m-2);
		G[to].push_back(m - 1);
	}

	void Init(){
		edge.clear();
		oriEdge.clear();
		for (int i = 0; i < 5050; i++) G[i].clear();
		memset(in,0,sizeof(in));
		memset(out, 0, sizeof(out));
		cin >> n >> m;
		for (int i = 0; i < m; i++){
			int a, b;
			char c;
			cin >> a >> b >> c;
			if (c == 'U'){
				addEdge(edge, a, b, 1, 0);
			}
			else{
				oriEdge.push_back(Edge(a, b, 0, 0));
			}
			out[a]++;
			in[b]++;
		}
	}

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

	int MaxFlow(int start,int end){
		int flow=0;
		while (true){
			int Flow[5050];
			int parent[5050];
			memset(Flow, 0, sizeof(Flow));
			Flow[0] = Inf;
			queue<int> q;
			q.push(start);
			while (!q.empty()){
				int id = q.front();
				q.pop();
				for (int i = 0; i < G[id].size(); i++){
					int ide = G[id][i];
					int to = edge[ide].to;
					if (!Flow[to] && edge[ide].cap>edge[ide].flow){
						Flow[to] = min(Flow[id], edge[ide].cap - edge[ide].flow);
						parent[to] = ide;
						q.push(to);
					}
				}
				if (Flow[end]) break;
			}
			if (!Flow[end]) break;
			flow += Flow[end];
			for (int i = end; i != start; i = edge[parent[i]].from){
				edge[parent[i]].flow += Flow[end];
				edge[parent[i] ^ 1].flow -= Flow[end];
			}
		}
		return flow;
	}
	void find_path(int u, vector<int>&path)
	{
		for (int i = 0; i < G[u].size(); ++i)
		{
			int x = G[u][i];
			if (visit[x]) continue;
			visit[x] = true;
			Edge&e = oriEdge[x];
			find_path(e.to, path);
			path.push_back(e.to);
		}
	}

	void Deal(){
		Init();
		if (!judge()){
			cout << "No euler circuit exist" << endl;
			return;
		}
		for (int i = 1; i <= n; i++){
			if (out[i] > in[i]) addEdge(edge, 0, i, (out[i]-in[i])/2, 0);
			if (in[i] > out[i]) addEdge(edge, i, n + 1, (in[i]-out[i])/2, 0);
		}
		int total = 0;
		for (int i = 0; i < G[0].size(); i++) total += edge[G[0][i]].cap;
		int res = MaxFlow(0, n + 1);
		if (res != total){
			cout << "No euler circuit exist" << endl;
			return;
		}
		for (int i = 1; i <= n; i++){
			for (int j = 0; j < G[i].size(); j++){
				Edge& e = edge[G[i][j]];
				if (e.cap == 0) continue;
				if (e.from == 0 || e.from == n + 1 || e.to == 0 || e.to == n + 1) continue;
				if (e.flow < 1) oriEdge.push_back(Edge(e.from,e.to,0,0));
				else oriEdge.push_back(Edge(e.to, e.from, 0, 0));
			}
		}
		for (int i = 0; i < 5050; i++) G[i].clear();
		for (int i = 0; i < oriEdge.size(); i++){
			int id = oriEdge[i].from;
			G[id].push_back(i);
		}
		vector<int> ans;
		memset(visit, 0, sizeof(visit));
		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;
	Solve a;
	while (T--){
		a.Deal();
		if (T) cout << endl;
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值