poj 1637 判斷混合圖是否存在歐拉回路
阿新 • • 發佈:2019-02-19
題意: 就是對有無向邊和有向邊的混合圖,判斷存不存在歐拉回路。
若圖G中存在這樣一條路徑,使得它恰通過G中每條邊一次,則稱該路徑為尤拉路徑。若該路徑是一個圈,則稱為尤拉(Euler)迴路。
參考下面的解釋:
【混合圖】
混合圖(既有有向邊又有無向邊的圖)中尤拉環、尤拉路徑的判定需要藉助網路流!
(1)尤拉環的判定:
一開始當然是判斷原圖的基圖是否連通,若不連通則一定不存在尤拉環或尤拉路徑(不考慮度數為0的點)。
其實,難點在於圖中的無向邊,需要對所有的無向邊定向(指定一個方向,使之變為有向邊),使整個圖變成一個有向尤拉圖(或有向半尤拉圖)。若存在一個定向滿足此條件,則原圖是尤拉圖(或半尤拉圖)否則不是。關鍵就是如何定向?
首先給原圖中的每條無向邊隨便指定一個方向(稱為初始定向),將原圖改為有向圖G',然後的任務就是改變G'中某些邊的方向(當然是無向邊轉化來的,原混合圖中的有向邊不能動)使其滿足每個點的入度等於出度。
設D[i]為G'中(點i的出度 - 點i的入度)。
若初始D值都是偶數,則將G'改裝成網路:設立源點S和匯點T,對於每個D[i]>0的點i,連邊<S, i>,容量為D[i]/2;對於每個D[j]<0的點j,連邊<j, T>,容量為-D[j]/2;G'中的每條邊在網路中仍保留,容量為1(表示該邊最多隻能被改變方向一次)。求這個網路的最大流,若S引出的所有邊均滿流,則原混合圖是尤拉圖,將網路中所有流量為1的中間邊(就是不與S或T關聯的邊)在G'中改變方向,形成的新圖G''一定是有向尤拉圖;若S引出的邊中有的沒有滿流,則原混合圖不是尤拉圖。
為什麼能這樣建圖?
考慮網路中的一條增廣路徑S-->i-->...-->j-->T,將這條從i到j的路徑在G'中全部反向,則:i的入度加1出度減1,j的入度減1出度加1,路徑中其它點的入度出度均不變。而i是和S相連的,因此初始D[i]>0,即i的出度大於入度,故這樣反向之後D[i]減少2;同理,j是和T相連的,這樣反向之後D[j]增加2。因此,若最大流中邊<S, i>滿流(流量為初始D[i]/2),此時D[i]值就變成了0,也就是i的入度等於出度。因此只要使所有S引出的邊全部滿流,所有初始D值>0的點的D值將等於0,又因為將邊變向後所有點的D值之和不變,所有初始D值小於0的點的D值也將等於0,而初始D值等於0的D點既不與S相連也不與T相連,所以它們是網路中的中間點,而中間點的流入量等於流出量,故它們的入度和出度一直不變,即D值一直為0。因此,整個圖G'成為尤拉圖。
(2)尤拉路徑的判定:
首先可以想到的是列舉尤拉路徑的起點i和終點j,然後在圖中新增邊<j, i>,再求圖中是否有歐拉回路即可。但是,該演算法的時間複雜度達到了O(M * 最大流的時間),需要優化。
前面已經說過,在將邊變向的過程中任何點的D值的奇偶性都不會改變,而一個有向圖有尤拉路徑的充要條件是基圖連通且有且只有一個點的入度比出度少1(作為尤拉路徑的起點),有且只有一個點的入度比出度多1(作為終點),其餘點的入度等於出度。這就說明,先把圖中的無向邊隨便定向,然後求每個點的D值,若有且只有兩個點的初始D值為奇數,其餘的點初始D值都為偶數,則有可能存在尤拉路徑(否則不可能存在)。進一步,檢查這兩個初始D值為奇數的點,設為點i和點j,若有D[i]>0且D[j]<0,則i作起點j作終點(否則若D[i]與D[j]同號則不存在尤拉路徑),連邊<j, i>,求是否存在尤拉環即可(將求出的尤拉環中刪去邊<j, i>即可)。這樣只需求一次最大流。
總結 :
首先是對圖中的無向邊隨意定一個方向,然後統計每個點的入度(indeg)和出度(outdeg),如果(indeg - outdeg)是奇數的話,一定不存在歐拉回路;
如果所有點的入度和出度之差都是偶數,那麼就開始網路流構圖:
1,對於有向邊,捨棄;對於無向邊,就按照最開始指定的方向建權值為 1 的邊;
2,對於入度小於出度的點,從源點連一條到它的邊,權值為(outdeg - indeg)/2;出度小於入度的點,連一條它到匯點的權值為(indeg - outdeg)/2 的邊;
構圖完成,如果滿流(求出的最大流值 == 和匯點所有連邊的權值之和),那麼存在歐拉回路,否則不存在。
#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
#include<cmath>
#include<cstdlib>
#include<stack>
#include<queue>
#include <iomanip>
#include<iostream>
#include<algorithm>
using namespace std ;
const int N=300 ;
const int M=25000 ;
const int inf=1<<30 ;
struct node
{
int u ,v,c,next ;
}edge[M] ;
int head[N],dis[N],pre[N],cur[N],gap[N] ;
int top ,out[N],in[N];
void add(int u ,int v,int c)
{
edge[top].u=u;
edge[top].v=v;
edge[top].c=c;
edge[top].next=head[u];
head[u]=top++;
edge[top].u=v;
edge[top].v=u;
edge[top].c=0;
edge[top].next=head[v];
head[v]=top++;
}
void sap(int s,int t,int nv)
{
memset(dis,0,sizeof(dis));
memset(gap,0,sizeof(gap));
int u,v,minflow=inf, flow=0 ;
for(int i = 0 ; i <=nv ; i++) cur[i]=head[i] ;
u=pre[s]=s;
gap[s]=nv;
while(dis[s] < nv )
{
loop :
for(int &j=cur[u] ; j!=-1 ; j=edge[j].next)
{
v=edge[j].v ;
if(edge[j].c > 0 && dis[u] == dis[v] +1 )
{
minflow = min(minflow ,edge[j].c) ;
pre[v]=u;
u=v ;
if(v==t)
{
for( u =pre[v] ; v!=s ;v=u,u=pre[u])
{
edge[cur[u]].c -= minflow ;
edge[cur[u]^1].c += minflow ;
}
flow += minflow ;
minflow = inf ;
}
goto loop ;
}
}
int mindis=nv ;
for(int i = head[u] ; i!=-1 ; i=edge[i].next)
{
v=edge[i].v ;
if(edge[i].c > 0 && dis[v] < mindis)
{
mindis = dis[v] ;
cur[u]=i ;
}
}
if(--gap[dis[u]]==0) break ;
gap[ dis[u] = mindis +1 ]++ ;
u=pre[u] ;
}
}
int main()
{
int T ,n,m,u,v,w;
scanf("%d",&T) ;
while(T--)
{
top = 0 ;
memset(head,-1,sizeof(head)) ;
memset(out,0,sizeof(out)) ;
memset(in,0,sizeof(in)) ;
scanf("%d%d",&n,&m) ;
int s = 0 , t=n+1 ;
for(int i = 1 ; i <= m ; i++)
{
scanf("%d%d%d",&u,&v,&w) ;
out[u]++ ; in[v]++ ;//隨意定個方向
if(w==0) // 雙向
add(u,v,1) ;
}
int flag = 0 ;
for(int i = 1 ; i <= n; i++)
{
if( (out[i]-in[i]) > 0)
add(s,i,(out[i]-in[i])/2 ) ;
else if( (out[i]-in[i]) < 0)
add(i,t,-(out[i]-in[i])/2) ;
if( (out[i]-in[i]) & 1)//是奇數,不存在
{
flag = 1 ; break;
}
}
if(flag)
{
printf("impossible\n");
continue;
}
sap(s,t,t+1) ;
for(int i = head[s] ; i !=-1 ; i=edge[i].next)
{
if(edge[i].c != 0)//不是滿流
{
flag=1 ; break ;
}
}
if(flag)
printf("impossible\n");
else printf("possible\n");
}
return 0 ;
}