前几天由于断网一直把写博的事情耽误了,现在可以继续了~
POJ 1637 Sightseeing Tour
题意:求混合图的欧拉回路。
方法:用最大流解决,见下文。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<fstream>
#include<memory>
#define MAXV 1005
#define MAXE 100005
using namespace std;
const int inf=1<<30-1;
int du[MAXV];
int sum,ans,pos,s,t;
int dist[MAXV]= {0}, numbs[MAXV] = {0}, src, des, n;
const int infinity = inf;
struct edge{
int ver; // vertex
int cap; // capacity
int flow; // current flow in this arc
edge *next; // next arc
edge *rev; // reverse arc
edge(){}
edge(int Vertex, int Capacity, edge *Next)
:ver(Vertex), cap(Capacity), flow(0), next(Next), rev((edge*)NULL){}
void* operator new(size_t, void *p){
return p;
}
}*Net[MAXV];
void rev_BFS(){
int Q[MAXV], head = 0, tail = 0;
for(int i=1; i<=n; ++i){
dist[i] = MAXV;
numbs[i] = 0;
}
Q[tail++] = des;
dist[des] = 0;
numbs[0] = 1;
while(head != tail){
int v = Q[head++];
for(edge *e = Net[v]; e; e = e->next){
if(e->rev->cap == 0 || dist[e->ver] < MAXV)continue;
dist[e->ver] = dist[v] + 1;
++numbs[dist[e->ver]];
Q[tail++] = e->ver;
}
}
}
int maxflow(){
int u, totalflow = 0;
edge *CurEdge[MAXV], *revpath[MAXV];
for(int i=1; i<=n; ++i)CurEdge[i] = Net[i];
u = src;
while(dist[src] < n){
if(u == des){ // find an augmenting path
int augflow = infinity;
for(int i = src; i != des; i = CurEdge[i]->ver)
augflow = min(augflow, CurEdge[i]->cap);
for(int i = src; i != des; i = CurEdge[i]->ver){
CurEdge[i]->cap -= augflow;
CurEdge[i]->rev->cap += augflow;
CurEdge[i]->flow += augflow;
CurEdge[i]->rev->flow -= augflow;
}
totalflow += augflow;
u = src;
}
edge *e;
for(e = CurEdge[u]; e; e = e->next)
if(e->cap > 0 && dist[u] == dist[e->ver] + 1)break;
if(e){ // find an admissible arc, then Advance
CurEdge[u] = e;
revpath[e->ver] = e->rev;
u = e->ver;
} else { // no admissible arc, then relabel this vertex
if(0 == (--numbs[dist[u]]))break; // GAP cut, Important!
CurEdge[u] = Net[u];
int mindist = n;
for(edge *te = Net[u]; te; te = te->next)
if(te->cap > 0)mindist = min(mindist, dist[te->ver]);
dist[u] = mindist + 1;
++numbs[dist[u]];
if(u != src)
u = revpath[u]->ver; // Backtrack
}
}
return totalflow;
}
int main()
{
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
int Case,m,u,v,x,i,j,ok;
scanf("%d",&Case);
while(Case--){
memset(du,0,sizeof(du));
memset(Net,0,sizeof(Net));
memset(numbs,0,sizeof(numbs));
memset(dist,0,sizeof(dist));
scanf("%d%d",&n,&m);
ok=1;
s=n+1;t=n+2;
src = s; des =t;
edge *buffer = new edge[MAXE];
edge *data = buffer;
while(m--){
scanf("%d%d%d",&u,&v,&x);
if(u==v)continue;
du[u]++;du[v]--;
if(!x) {
Net[u] = new((void*) data++) edge(v, inf, Net[u]);
Net[v] = new((void*) data++) edge(u, 0, Net[v]);
Net[u]->rev = Net[v];
Net[v]->rev = Net[u];
}
}
for(i=1;i<=n;i++){
if((du[i]+1000)%2)
break;
du[i]/=2;
if(du[i]>0){
Net[s] = new((void*) data++) edge(i,du[i], Net[s]);
Net[i] = new((void*) data++) edge(s, 0, Net[i]);
Net[s]->rev = Net[i];
Net[i]->rev = Net[s];
sum+=du[i];
}
else{
Net[i] = new((void*) data++) edge(t,-du[i], Net[i]);
Net[t] = new((void*) data++) edge(i, 0, Net[t]);
Net[i]->rev = Net[t];
Net[t]->rev = Net[i];
}
}
if(i<=n)
printf("impossible\n");
else {
n+=2;
rev_BFS();
int ans=maxflow();
if(ans==sum) printf("possible\n");
else printf("impossible\n");
}
sum=0;
delete [] buffer;
}
return 0;
}
欧拉路:
欧拉通路 (Euler tour)——通过图中每条边一次且仅一次,并且过每一顶点的通路。
欧拉回路 (Euler circuit)——通过图中每条边一次且仅一次,并且过每一顶点的回路。
欧拉回路的判断:欧拉图——存在欧拉回路的图。
此图的基图连通的条件下:
无向图存在欧拉回路条件
一个无向图存在欧拉回路,当且仅当该图所有顶点度数都是偶数。有向图存在欧拉回路条件
一个有向图存在欧拉回路,且所有顶点的入度等于出度混合图存在欧拉回路条件
假设有一张图有向图G',在不论方向的情况下它与G同构。并且G'包含了G的所有有向边。那么如果存在一个图G'使得G'存在欧拉回路,那么G就存在欧拉回路。
具体思路
把该图的无向边随便定向,计算每个点的入度和出度。如果有某个点出入度之差为奇数, 那么肯定不存在欧拉回路。因为欧拉回路要求每点入度 = 出度,也就是总度数为偶数,存在奇数度点必不能有欧拉回路。 好了,现在每个点入度和出度之差均为偶数。那么将这个偶数除以2,得x。 也就是说,对于每一个点,只要将x条边改变方向(入>出就是变入,出>入就是变出), 就能保证出 = 入。如果每个点都是出 = 入,那么很明显,该图就存在欧拉回路。 现在的问题就变成了:我该改变哪些边,可以让每个点出 = 入?构造网络流模型。首先,有向边是不能改变方向的,要之无用,删。 一开始不是把无向边定向了吗?定的是什么向,就把网络构建成什么样,边长容量上限1(说inf的果然不对嘛…)。另新建s和t。对于入 > 出的点u,连接边(u, t)、容量为x, 对于出 > 入的点v,连接边(s, v),容量为x(注意对不同的点x不同)。之后,察看是否有满流的分配。有就是能有欧拉回路,没有就是没有。欧拉回路是哪个? 察看流值分配,将所有流量非0(上限是1,流值不是0就是1)的边反向,就能得到每点入度 = 出度的欧拉图。 由于是满流,所以每个入 > 出的点,都有x条边进来,将这些进来的边反向,OK,入 = 出了。对于出 > 入的点亦然。 那么,没和s、t连接的点怎么办?和s连接的条件是出 > 入,和t连接的条件是入 > 出,那么这个既没和s也没和t连接的点,自然早在开始就已经满足入 = 出了。 那么在网络流过程中,这些点属于“中间点”。我们知道中间点流量不允许有累积的,这样,进去多少就出来多少,反向之后,自然仍保持平衡。 所以,就这样,混合图欧拉回路问题,解了。混合图的欧拉回路, 既然是欧拉回路首先图必须是连通的:将所有边看成无向边做一遍dfs判断连通性。要构成欧拉图最后所以得点入度都等于出度,即需要每个点入度和出度之差为偶数,若有两个点为奇数可能有欧拉路,这时只需要在这两个点之间加一条边,即可转化为欧拉回路问题。判断完毕后,如果所有点入度和出度差为偶数,构建网络流图,原来图中的有向边,可以删去。
转自:http://www.cppblog.com/yuan1028/archive/2010/07/31/121748.html
杯了个具,codeforce又要掉rating了ORZ…
晚安世界,以后比赛还是不只切图论了吧,笨蛋……