您的位置:首页 > 产品设计 > UI/UE

UVa 10735 (混合图的欧拉回路) Euler Circuit

2015-02-20 10:59 337 查看
题意:

给出一个图,有的边是有向边,有的是无向边。试找出一条欧拉回路。

分析:

按照往常的思维,遇到混合图,我们一般会把无向边拆成两条方向相反的有向边。

但是在这里却行不通了,因为拆成两条有向边的话,就表示这个边能“在两个相反方向各经过一次”。

而题意是这个边只能经过一次。

假设图中存在欧拉回路,则所有点的出度out(i) 等于 入度in(i)

不妨这样,先将所有的无向边任意定向,对于out(u) > in(u)的点,可以将已经定向的无向边u->v反向为v->u,这样out(u) - in(u)的值减2

如果把出度看做“货物”,则out(u) > in(u)的点提供货物,out(u) < in(u)的点需要货物,所以我们可以用网络流求最大流的算法,来使“供需平衡”

具体来说就是,给已经定向的无向边两点之间连一条容量为1的边,连接源点 与 所有“提供”出度的点,连接 所有“需要”出度的点 与 汇点。

如果求出来的最大流满载,也就是所有的出度都能被运到需要的地方,则有解。

在最大流中,如果流量为1则代表将该边反向的操作。

所以再建一个新图来求欧拉回路。

本题还有一个坑点就是可能存在平行边,所以求欧拉路径的过程中用 vis[u][v] = 1;的方法是行不通的了。

#include <bits/stdc++.h>
#define REP(i,n) for(int i = 0; i < (n); i++)
#define PB push_back
using namespace std;

const int INF = 1000000000;
const int maxn = 500 + 10;

struct Edge
{
int from, to, cap, flow;
Edge(int u, int v, int c, int f):from(u), to(v), cap(c), flow(f) {}
};

struct EdmondsKarp
{
int n, m;
vector<Edge> edges;
vector<int> G[maxn];
int a[maxn], p[maxn];

void Init(int n)
{
REP(i, n) G[i].clear();
edges.clear();
}

void AddEdge(int from, int to, int cap)
{
edges.PB(Edge(from, to, cap, 0));
edges.PB(Edge(to, from, 0, 0));
m = edges.size();
G[from].PB(m-2);
G[to].PB(m-1);
}

int MaxFlow(int s, int t)
{
int flow = 0;
for(;;)
{
queue<int> Q;
Q.push(s);
memset(a, 0, sizeof(a));
a[s] = INF;
while(!Q.empty())
{
int x = Q.front(); Q.pop();
REP(i, G[x].size())
{
Edge& e = edges[G[x][i]];
if(!a[e.to] && e.cap > e.flow)
{
a[e.to] = min(a[x], e.cap - e.flow);
p[e.to] = G[x][i];
Q.push(e.to);
}
}
if(a[t]) break;
}
if(!a[t]) break;
for(int u = t; u != s; u = edges[p[u]].from)
{
edges[p[u]].flow += a[t];
edges[p[u]^1].flow -= a[t];
}
flow += a[t];
}
return flow;
}
}g;

int n, m;
int deg[maxn], u[maxn], v[maxn], id[maxn];
bool directed[maxn];

vector<int> G[maxn];//建新图,用来求欧拉回路
vector<int> vis[maxn];
vector<int> path;//欧拉回路

void Euler(int u)
{
REP(i, G[u].size()) if(!vis[u][i])
{
vis[u][i] = 1;
Euler(G[u][i]);
path.PB(G[u][i]+1);
}
}

void print_answer()
{
REP(i, n) { G[i].clear(); vis[i].clear(); }
REP(i, m)
{
bool rev = false;
if(!directed[i] && g.edges[id[i]].flow > 0) rev = true;//流量为1对应将该边反向
if(!rev) { G[u[i]].PB(v[i]); vis[u[i]].PB(0); }
else { G[v[i]].PB(u[i]); vis[v[i]].PB(0); }
}

path.clear();
Euler(0);
printf("1");
for(int i = path.size()-1; i >= 0; i--) printf(" %d", path[i]);
puts("");
}

int main()
{
//freopen("in.txt", "r", stdin);

int T;
scanf("%d", &T);
while(T--)
{
scanf("%d%d", &n, &m);
g.Init(n+2);
memset(deg, 0, sizeof(deg));
REP(i, m)
{
char d;
scanf("%d %d %c", &u[i], &v[i], &d);
u[i]--; v[i]--;
directed[i] = (d == 'D');
deg[u[i]]++; deg[v[i]]--;
if(!directed[i]) { id[i] = g.edges.size(); g.AddEdge(u[i], v[i], 1); }//第i条边在网络流中的编号
}

bool ok = true;
REP(i, m) if(deg[i] % 2 != 0) { ok = false; break; }//出入度之和不是偶数说明不存在欧拉回路

int s = n, t = n+1;
if(ok)
{
int sum = 0;
REP(i, n)
{
if(deg[i] > 0) { sum += deg[i] / 2; g.AddEdge(s, i, deg[i] / 2); }
if(deg[i] < 0) { g.AddEdge(i, t, -deg[i] / 2); }
}
int flow = g.MaxFlow(s, t);
if(flow != sum) ok = false;//最大流不满载
}

if(ok) print_answer(); else puts("No euler circuit exist");
if(T) puts("");
}

return 0;
}


代码君
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: