G - Sightseeing Tour ZOJ - 1992
The city executive board in Lund wants to construct a sightseeing tour by bus in Lund, so that tourists can see every corner of the beautiful city. They want to construct the tour so that every street in the city is visited exactly once. The bus should also start and end at the same junction. As in any city, the streets are either one-way or two-way, traffic rules that must be obeyed by the tour bus. Help the executive board and determine if it's possible to construct a sightseeing tour under these constraints.
Input
On the first line of the input is a single positive integer n, telling the number of test scenarios to follow. Each scenario begins with a line containing two positive integers m and s, 1 <= m <= 200, 1 <= s <= 1000 being the number of junctions and streets, respectively. The following s lines contain the streets. Each street is described with three integers, xi, yi, and di, 1 <= xi, yi <= m, 0 <= di <= 1, where xi and yi are the junctions connected by a street. If di = 1, then the street is a one-way street (going from xi to yi), otherwise it's a two-way street. You may assume that there exists a junction from where all other junctions can be reached.
Output
For each scenario, output one line containing the text "possible" or "impossible", whether or not it's possible to construct a sightseeing tour.
Sample Input
4
5 8
2 1 0
1 3 0
4 1 1
1 5 0
5 4 1
3 4 0
4 2 1
2 2 0
4 4
1 2 1
2 3 0
3 4 0
1 4 1
3 3
1 2 0
2 3 0
3 2 0
3 4
1 2 0
2 3 1
1 2 0
3 2 0
Sample Output
possible
impossible
impossible
possible
欧拉回路是指从某结点出发,不重复地经过所有的路径,并且最后能回到出发点的路径;
本题就是判断是否存在欧拉回路。
首先保证是在连通图的前提下
无向图
欧拉路径:度数为奇数的点有0个或2个
欧拉回路:度数为奇数的点有0个
有向图:
欧拉路径:有两个点入度不等于出度,其他点入度等于出度,且一个点入度-出度=1,另一个点出度-入度=1,出度大的为起点;或者所有点入度等于出度
欧拉回路:所有点入度等于出度
混合图:把无向边转化为有边判断
开始时把有无向边转为有向边,方向随便,权值(容量)为1,记录每个点的出度out[i],入度in[i],如果
(1)out[i]-in[i]为奇数,那么无论怎么转换无向边的方向都不可能让out[i]==in[i],因为与i点相连的边转变方向后,out[i]-in[i]值只能偶数倍的增减,这种情况是不可能存在欧拉回路的
(2)out[i]-in[i]为偶数,那么需要改变与i相连的(out[i]-in[i])/2条边的方向,才能使i的出度等于入度,所以建图只加入无向边。创建源点sn=0,源点连向out[i]>in[i]的点,权值(容量)为(out[i]-in[i])/2, 汇点为tn=n+1,out[i]<in[i]的点连向汇点tn,权值(容量)为(in[i]-out[i])/2,如果满流,则存在欧拉回路。
为什么?
在满流的图(无向边建的有向图,可调整边的方向)中,如果把所有(除与源点和与汇点相连的边外)求最大流中找到的增广路径经过的边的方向都取反,那么所有out[i]>in[i](与源点相连)的点变成out[i]==in[i],同样out[i]<in[i]的点变成out[i]==in[i];如对于sn=》i ( (out[i]-in[i])/2>0) =》ti(ti={ t[i] | out[t] == in[t]})=》j( (out[j]-in[j])/2<0) =》tn , =》表示多条边,因为out[i]>in[i],所以要改变i=》t的方向,同时,为保证ti的长度等于入度,必须同时改变同等数量边i=》j的方向。
由此可推断出:改变点i(out[i]>in[i])的出度的边的方向,必须同时改变该边所在的可行流所在的所有边,那么是否可以改变所有点i(out[i]>in[i])的出度的边一半(sum)的方向,就等价于判断最大流必须等于sum。同样对于 (out[j]-in[j])/2<0的点,由于每一条边既贡献一个入度,又贡献一个出度,所以图中所有点的入度数的和等于所有点出度的和,那么所有(out[j]-in[j])<0 的点数量等于 (out[i]-in[i])>0的点,那么 (out[i]-in[i])/2==-(out[i]-in[i])/2=sum ,所以只要sum==最大流,即满流,就能实现所有点入度等于出度
#include<cstdio>
#include<stack>
#include<set>
#include<vector>
#include<queue>
#include<algorithm>
#include<cstring>
#include<string>
#include<map>
#include<iostream>
#include<cmath>
using namespace std;
#define inf 0x3f3f3f3f
typedef long long ll;
const int N=225;
const int mmax = 3020+ 7;
int num,n,sn,tn,m;
int head[N],cur[N],dep[N];
struct node
{
int v,w,next;
} gra[mmax];
int in[N],out[N];
void ad(int u,int v,int w)
{
gra[num].v=v;
gra[num].next=head[u];
gra[num].w=w;
head[u]=num++;
gra[num].next=head[v];
gra[num].v=u;
gra[num].w=0;
head[v]=num++;
}
int bfs()
{
queue<int>que;
memset(dep,0,sizeof(dep));
que.push(sn);
dep[sn]=1;
while(!que.empty())
{
int u=que.front();
que.pop();
if(u==tn)
return 1;
for(int i=head[u]; i!=-1; i=gra[i].next)
{
if(!dep[gra[i].v]&&gra[i].w>0)
{
dep[gra[i].v]=dep[u]+1;
que.push(gra[i].v);
}
}
}
if(dep[tn])
return 1;
return 0;
}
int dfs(int u,int minl)
{
if(u==tn)
return minl;
for(int &i=cur[u]; i!=-1; i=gra[i].next)
{
if(dep[gra[i].v]==dep[u]+1&&gra[i].w>0)
{
int k=dfs(gra[i].v,min(minl,gra[i].w));
if(k>0)
{
gra[i].w-=k;
gra[i^1].w+=k;
return k;
}
}
}
return 0;
}
int danic()
{
int ans=0;
while(bfs())
{
for(int i=0; i<=tn; i++)
{
cur[i]=head[i];
}
while(int flow=dfs(0,inf))
{
ans+=flow;
}
}
return ans;
}
void init()
{
memset(head,-1,sizeof(head));
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
num=0;
}
int main()
{
int t,x,y,z;
scanf("%d",&t);
while(t--)
{
init();
scanf("%d%d",&n,&m);
for(int i=1; i<=m; i++)
{
scanf("%d%d%d",&x,&y,&z);
out[x]++;
in[y]++;
if(z!=1)
ad(x,y,1);
}
int flag=1;
for(int i=1; i<=n; i++)
{
if((out[i]-in[i])%2!=0)
{
flag=0;
break;
}
}
if(flag==0)
printf("impossible\n");
else
{
sn=0;
tn=n+1;
int sum=0;
for(int i=1; i<=n; i++)
{
int k=out[i]-in[i];
if(k>0)
{
ad(0,i,k/2); //k:要改变的边的数量
sum+=k/2;
}
else if(k<0)
ad(i,n+1,-k/2);
}
if(sum==danic()) //dani()求最大流模板求法
printf("possible\n");
else
printf("impossible\n");
}
}
return 0;
}