HDU 3472 混合图欧拉回路的判定

来源:互联网 发布:网络最火名言 编辑:程序博客网 时间:2024/05/22 17:50

HS BDC

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 792    Accepted Submission(s): 307


Problem Description
IELTS is around the corner! love8909 has registered for the exam, but he still hasn’t got prepared. Now he decides to take actions. But when he takes out the New Oriental IELTS Vocabulary, he finds there are so many words. But love8909 doesn’t get scared, because he has got a special skill. If he can make a list with some meaningful words, he will quickly remember these words and will not forget them. If the last letter of some word Wa is the same as the first letter of some word Wb, then you can connect these two words and make a list of two words. If you can connect a word to a list, you will make a longer list.

While love8909 is making the list, he finds that some words are still meaningful words if you reverse them. For example, if you reverse the word “pat”, you will get another meaningful word “tap”.

After scanning the vocabulary, love8909 has found there are N words, some of them are meaningful if reversed, while others are not. Now he wonders whether he can remember all these words using his special skill.

The N-word list must contain every word once and only once.
 

Input
An integer T (T <= 50) comes on the first line, indicating the number of test cases.

On the first line of each test cases is an integer N (N <= 1000), telling you that there are N words that love8909 wants to remember. Then comes N lines. Each of the following N lines has this format: word type. Word will be a string with only ‘a’~’z’, and type will be 0(not meaningful when reversed) or 1(meaningful when reversed). The length of each word is guaranteed to be less than 20.

 

Output
The format of the output is like “Case t: s”, t is the number of the test cases, starting from 1, and s is a string.
For each test case, if love8909 can remember all the words, s will be “Well done!”, otherwise it’s “Poor boy!”

 

Sample Input
36aloha 0arachnid 0dog 0gopher 0tar 1tiger 03thee 1earn 0nothing 02pat 1acm 0
 

Sample Output
Case 1: Well done!Case 2: Well done!Case 3: Poor boy!
Hint
In the first case, the word “tar” is still meaningful when reversed, and love8909 can make a list as “aloha-arachnid-dog-gopher-rat-tiger”. In the second case, the word “thee” is still meaningful when reversed, and love8909 can make a list as “thee-earn-nothing”. In the third case, no lists can be created.
 

Author
allenlowesy
 

Source
2010 ACM-ICPC Multi-University Training Contest(4)——Host by UESTC
 


【混合图】
混合图(既有有向边又有无向边的图)中欧拉环、欧拉路径的判定需要借助网络流!

(1)欧拉环的判定:
一开始当然是判断原图的基图是否连通,若不连通则一定不存在欧拉环或欧拉路径(不考虑度数为0的点)。

其实,难点在于图中的无向边,需要对所有的无向边定向(指定一个方向,使之变为有向边),使整个图变成一个有向欧拉图(或有向半欧拉图)。若存在一个定向满足此条件,则原图是欧拉图(或半欧拉图)否则不是。关键就是如何定向?

首先给原图中的每条无向边随便指定一个方向(称为初始定向),将原图改为有向图G',然后的任务就是改变G'中某些边的方向(当然是无向边转化来的,原混合图中的有向边不能动)使其满足每个点的入度等于出度。
设D[i]为G'中(点i的出度 - 点i的入度)。可以发现,在改变G'中边的方向的过程中,任何点的D值的奇偶性都不会发生改变(设将边<i, j>改为<j, i>,则i入度加1出度减1,j入度减1出度加1,两者之差加2或减2,奇偶性不变)!而最终要求的是每个点的入度等于出度,即每个点的D值都为0,是偶数,故可得:若初始定向得到的G'中任意一个点的D值是奇数,那么原图中一定不存在欧拉环!

若初始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>即可)。这样只需求一次最大流。

 

根据上面的解释,可以建图跑最大流判断。

代码:

/* ***********************************************Author :rabbitCreated Time :2014/3/26 10:45:50File Name :12.cpp************************************************ */#pragma comment(linker, "/STACK:102400000,102400000")#include <stdio.h>#include <iostream>#include <algorithm>#include <sstream>#include <stdlib.h>#include <string.h>#include <limits.h>#include <string>#include <time.h>#include <math.h>#include <queue>#include <stack>#include <set>#include <map>using namespace std;#define INF 0x3f3f3f3f#define eps 1e-8#define pi acos(-1.0)typedef long long ll;const int maxn=50;const int maxm=10000;struct Edge{int next,to,cap;Edge(){};Edge(int _next,int _to,int _cap){next=_next;to=_to;cap=_cap;}}edge[maxm];int head[maxn],tol,dep[maxn],gap[maxn];void addedge(int u,int v,int flow){    edge[tol]=Edge(head[u],v,flow);head[u]=tol++;    edge[tol]=Edge(head[v],u,0);head[v]=tol++;}void bfs(int start,int end){    memset(dep,-1,sizeof(dep));    memset(gap,0,sizeof(gap));    gap[0]++;int front=0,rear=0,Q[maxn];    dep[end]=0;Q[rear++]=end;    while(front!=rear){        int u=Q[front++];        for(int i=head[u];i!=-1;i=edge[i].next){            int v=edge[i].to;if(dep[v]==-1)                Q[rear++]=v,dep[v]=dep[u]+1,gap[dep[v]]++;        }    }}int sap(int s,int t,int N){int res=0;bfs(s,t);int cur[maxn],S[maxn],top=0,u=s,i;memcpy(cur,head,sizeof(head));while(dep[s]<N){if(u==t){int temp=INF,id;    for( i=0;i<top;i++)   if(temp>edge[S[i]].cap)   temp=edge[S[i]].cap,id=i;    for( i=0;i<top;i++)      edge[S[i]].cap-=temp,edge[S[i]^1].cap+=temp;    res+=temp;top=id;u=edge[S[top]^1].to;}if(u!=t&&gap[dep[u]-1]==0)break;for( i=cur[u];i!=-1;i=edge[i].next)if(edge[i].cap&&dep[u]==dep[edge[i].to]+1)break;if(i!=-1)cur[u]=i,S[top++]=i,u=edge[i].to;else{int MIN=N;for( i=head[u];i!=-1;i=edge[i].next)if(edge[i].cap&&MIN>dep[edge[i].to])MIN=dep[edge[i].to],cur[u]=i;--gap[dep[u]];++gap[dep[u]=MIN+1];if(u!=s)u=edge[S[--top]^1].to;}}return res;}int fa[maxn],in[maxn],out[maxn];int find(int x){if(fa[x]!=x)fa[x]=find(fa[x]);return fa[x];}void bing(int x,int y){int t1=find(x),t2=find(y);if(t1==t2)return;fa[t1]=t2;}int main(){     //freopen("data.in","r",stdin);     //freopen("data.out","w",stdout);     int T,t; scanf("%d",&T);     for(t=1;t<=T;t++){ int n; scanf("%d",&n); memset(in,0,sizeof(in)); memset(out,0,sizeof(out)); for(int i=0;i<=30;i++)fa[i]=i; memset(head,-1,sizeof(head));tol=0; char str[500];int k; int s=-1; while(n--){ scanf("%s%d",str,&k); int len=strlen(str); int u=str[0]-'a',v=str[len-1]-'a'; out[u]++;in[v]++; s=u; if(k==1)addedge(u,v,1); bing(u,v); } int flag=1,cnt=0,s1=-1,s2=-1; for(int i=0;i<26;i++) if(in[i]||out[i]){ if(find(i)!=find(s)){ flag=0;break; } if((in[i]+out[i])%2){ cnt++; if(s1==-1)s1=i; else s2=i; } }    if(cnt&&cnt!=2)flag=0;printf("Case %d: ",t);if(!flag)puts("Poor boy!");else{if(cnt==2){out[s1]++;in[s2]++;addedge(s1,s2,1);}int sum=0;for(int i=0;i<26;i++){int ret=out[i]-in[i];if(ret>0)addedge(26,i,ret/2),sum+=ret/2;else if(ret<0)addedge(i,27,-ret/2);}int ans=sap(26,27,30);if(ans==sum)puts("Well done!");else puts("Poor boy!");} }     return 0;}


0 0
原创粉丝点击