hdu 3472 HS BDC(混合欧拉路径)

题意:给出n个单词,一个单词的末尾字母和另一个单词的第一个字母相同,那么这两个单词就能连在一起,有的单词可以翻转,现在问能否把所有单词都连在一起并且每个单词使用一次且仅一次。


思路:把26个字母看成顶点,把每个单词看成一条边,如此建图,那么这个题就变成了判断这个图是否存在欧拉通路。我们知道欧拉通路的判断方法,但这题依然存在这问题,那就是单词可以翻转,也就是说,这个图中不仅存在有向边,而且还有无向边。这就变成了一个混合欧拉路径问题,可以通过网络流来解决这个问题。首先无向边任意选择一个方向,按有向边建图,然后统计每个顶点的入度和出度,入度和出度只差为奇数的点只能有0个或两个,否则无解。对于入度和出度只差为偶数的点,如果他们之间有无向边,那么把这条边反向,这个差就会缩小,问题就变成要把一些边反向,令入度和出度只差为0。把所有无向边按最开始规定的方向建图,每条边的容量为1,建立一个源点S和汇点T,对于出度>入度的顶点添加弧(S,u),容量为入度和出度的差值的一半(因为改一条边的方向这个差值会减少2),反之,添加(u,T)的弧。然后做最大流,判断是否满流。另外,还需要用并查集判断一下图的连通性。


代码:


#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-9
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn=1000+10;
struct Edge
{
    int from,to,cap,flow,next;
    Edge(){};
    Edge(int ff,int tt,int cc,int fl,int nx){from=ff;to=tt;cap=cc;flow=fl;next=nx;};
}edges[maxn<<2];
int ind[30],outd[30],parents[30];
int head[30],d[maxn],cur[maxn],nEdge;
bool vis[maxn];
void Init()
{
    memset(ind,0,sizeof(ind));
    memset(outd,0,sizeof(outd));
    memset(head,0xff,sizeof(head));
    for(int i=0;i<30;++i) parents[i]=i;
    nEdge=-1;
}
void AddEdges(int from,int to,int cap)
{
    edges[++nEdge]=Edge(from,to,cap,0,head[from]);
    head[from]=nEdge;
    edges[++nEdge]=Edge(to,from,0,0,head[to]);
    head[to]=nEdge;
}
int Find(int x)
{
    return x==parents[x]?x:parents[x]=Find(parents[x]);
}
bool BFS(int S,int T)
{
    memset(vis,0,sizeof(vis));
    memset(d,0,sizeof(d));
    queue<int>q;
    vis[S]=true;
    q.push(S);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int k=head[u];k!=-1;k=edges[k].next)
        {
            Edge e=edges[k];
            if(!vis[e.to]&&e.cap>e.flow)
            {
                d[e.to]=d[u]+1;
                vis[e.to]=true;
                q.push(e.to);
            }
        }
    }
    return vis[T];
}
int DFS(int u,int a,int T)
{
    if(u==T||a==0) return a;
    int flow=0,f;
    for(int & k=cur[u];k!=-1;k=edges[k].next)
    {
        Edge e=edges[k];
        if(d[e.to]==d[u]+1&&(f=DFS(e.to,min(a,e.cap-e.flow),T))>0)
        {
            edges[k].flow+=f;
            edges[k^1].flow-=f;
            flow+=f;a-=f;
            if(a==0) break;
        }
    }
    return flow;
}
int Maxflow(int S,int T)
{
    int flow=0;
    while(BFS(S,T))
    {
        for(int i=0;i<=T;++i) cur[i]=head[i];
        flow+=DFS(S,inf,T);
    }
    return flow;
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int t,tcase=0,n,type;
    char str[30];
    scanf("%d",&t);
    while(t--)
    {
        tcase++;
        Init();
        scanf("%d",&n);
        int cnt=0,u,v,len;
        bool flag=true;
        for(int i=0;i<n;++i)
        {
            scanf("%s%d",str,&type);
            len=strlen(str);
            u=(str[0]-'a'+1);
            v=(str[len-1]-'a'+1);
            if(type) AddEdges(u,v,1);
            ind[v]++;outd[u]++;
            if(Find(u)!=Find(v))
            {
                parents[Find(v)]=Find(u);
                cnt++;
            }
        }
        int S=0,T=27,odd=0,tmp,sum=0;
        for(int i=1;i<=26;++i)
        {
            if(ind[i]||outd[i])
            {
                cnt--;
                tmp=ind[i]-outd[i];
                if(tmp%2) odd++;
                else
                {
                    if(tmp>0) {AddEdges(i,T,tmp/2);sum+=(tmp/2);}
                    else AddEdges(S,i,(-tmp)/2);
                }
            }
        }
        if(cnt!=-1||(odd!=0&&odd!=2)) flag=false;
        tmp=Maxflow(S,T);
        printf("Case %d: ",tcase);
        if(tmp==sum&&flag)
            printf("Well done!\n");
        else
            printf("Poor boy!\n");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值