POJ1637 观光旅行
题目链接 : http://poj.org/problem?id=1637
Description:
给定一个 n 个节点, m 条边的混合图,有一些边是有向边,有一些边是无向边。
请给无向边定向,使得最后的有向图存在经过所有边仅一次的回路。并给出方案。
其中 1 ≤ n ≤ 200, 1 ≤ m ≤ 2000 。
模型:混合图欧拉回路问题 - 网络流
1.定义 : 混合欧拉回路问题,即在一张包含有向边和无向边的混合图中求欧拉回路
2.有向图欧拉回路判断:所有顶点的入度与出度之差为0。
3.混合图欧拉回路 :
首先,我们把无向边设定为任意一个方向
虽然不一定是欧拉回路
但是我们可以进行反悔操作
如果进行反悔操作
对于跟这条边连的点
入度++,出度--
或者
入度--,出度++
所以改变一条边的方向后
一个点入度/出度之差会+2或-2
所以在我们任意连边后,如果一个的(入度-出度)为奇数
那么它无论如何也不能变成出入度之差为0的点
于是impossible
然后接下来就要用到网络流了
我们不妨设一个点的入度为--\(in[v]\)出度为--\(ou[v]\)
对于一个点v
有一个非常重要的量\(tmp_v=ou[v]-in[v]\)
我们先用原始人的思维想一下
我们如果改变一条边 u -> v 的方向
那么\(tmp_u-=2 \&\& tmp_v+=2\)
所以我们可以超级暴力地枚举更换哪一条边的方向
就这么加加减减
直到所有的$ in[i]=ou[i]=0 $
然后可以考虑用网络流优化
引用自 https://blog.csdn.net/chenzhenyu123456/article/details/48302459
我们可以构建网络流模型,判断剩余度数能否完全分配。下面需要确定的是模型中边的方向.
假设我们定向原图中边<u, v>方向为u -> v。
1,当u的出度大于入度,才可能把多的出度分配给v,当做v的入度。这个度数能否成功分配可以用能否到达汇点sink来判定。
2,当u的出度小于入度,v是不可能把多的出度分配给u的,因为v->u没有边。这个时候我们必须要从source给点u分配度数。
这样建边方向就出来了,若确定无向边<u, v>方向为u -> v。
那么当点的in < out时,源点source向它建边,容量为差值的一半。当点的out > in时,它向汇点建边,容量为差值的一半。
建图:设置超级源点source,超级汇点sink。
1,对于边<u, v>,若它是无向的,我们人为定向,u向v建边,容量为1。当欧拉路径中是v -> u的,u多出的度数可以流到v。
2,若in[i] < out[i],则source向i建边,容量为正差值的一半;
3,若in[i] > out[i],则i向sink建边,容量为正差值的一半。
统计从source流入的流量sum,最后判断是否满流即可。
/*
一个并不能AC
但是把重要思想体现出来的代码
原谅我实在不想做POJ的题目QvQ
*/
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <queue>
#define rg register int
#define ll long long
#define RG register
#define il inline
using namespace std;
il int gi() {
rg x=0,o=0;RG char ch=getchar();
while(ch!='-'&&(ch<'0'||'9'<ch)) ch=getchar();
if(ch=='-') o=1,ch=getchar();
while('0'<=ch&&ch<='9') x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return o?-x:x;
}
#define N 501
#define M 20001
const int inf=214748364;
int s,t,n,m,Ans,sum;
int in[N],ou[N];
struct Edge { int to,nxt,w; } e[M];
int Ehead[N],Ecnt=2;
il void Eadd(int u,int v,int w) {
e[Ecnt]=(Edge){v,Ehead[u],w};
Ehead[u]=Ecnt++;
e[Ecnt]=(Edge){u,Ehead[v],0};
Ehead[v]=Ecnt++;
}
int lev[N],cur[M];
queue <int> Q;
il bool bfs() {
while(!Q.empty()) Q.pop();
memset(lev,-1,sizeof(lev));
Q.push(s),lev[s]=0;
while(!Q.empty()) {
int u=Q.front();
Q.pop();
for(int v,i=Ehead[u]; i; i=e[i].nxt) {
v=e[i].to;
if(e[i].w<=0 || lev[v]>0) continue;
lev[v]=lev[u]+1;
Q.push(v);
}
}
return lev[t]>0;
}
int dfs(int u,int f) {
if(u==t || !f) return f;
for(int &i=cur[u]; i; i=e[i].nxt) {
int v=e[i].to,di=0;
if(lev[v]!=lev[u]+1) continue;
if(di=dfs(v,min(f,e[i].w))) {
e[i].w-=di;
e[i^1].w+=di;
return di;
}
}
return 0;
}
il void maxflow() {
int di;
while(bfs()) {
for(int i=s; i<=t; ++i) cur[i]=Ehead[i];
Ans+=dfs(s,inf);
}
}
int main() {
n=gi(),m=gi();
s=0,t=n+1;
for(int u,v,w,i=1;i<=m;++i) {
u=gi(),v=gi(),w=gi();
++in[v],++ou[u];
if(!w) Eadd(u,v,1);
}
for(int tmp,i=1; i<=n; ++i) {
tmp=abs(in[i]-ou[i]);
if(tmp&1) {
puts("Impossible");
return 0;
}
tmp>>=1;
if(in[i]<ou[i]) sum+=tmp, Eadd(s, i, tmp);
else Eadd(i, t, tmp);
}
maxflow();
Ans==sum?puts("possible"):puts("impossible");
return 0;
}