HDU 3472 HS BDC(混合欧拉图(使用最大流))模板

HS BDC

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


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
  
  
3 6 aloha 0 arachnid 0 dog 0 gopher 0 tar 1 tiger 0 3 thee 1 earn 0 nothing 0 2 pat 1 acm 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
题意:给n个字符串,0:表示首尾的一个字符不能对换,1:表示可以对换。如果一个字符串的尾字符与另一个字符串的首字符相同则可以接在一起,问能否把所有的字符串接成一个串。
解题:如果把一个字符串的道字符看成起点u,尾字符看成终点v,对与0:则是一个有向边,对于1 则是双向边。这题就是一笔画成,但是分有向和无向,所以是混合欧拉图(1)首先,判断所有出现的点是不是在一个连通块。(2)顶点的总度数为奇度顶点的个数是否有0个或恰好有2个。(3)建图,用最大流,是否满流来判断是否可以。
#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
using namespace std;
#define captype int

const int MAXN = 100010;   //点的总数
const int MAXM = 400010;    //边的总数
const int INF = 1<<30;
struct EDG{
    int to,next;
    captype cap,flow;
} edg[MAXM];
int eid,head[MAXN];
int gap[MAXN];  //每种距离(或可认为是高度)点的个数
int dis[MAXN];  //每个点到终点eNode 的最短距离
int cur[MAXN];  //cur[u] 表示从u点出发可流经 cur[u] 号边
int pre[MAXN];

void init(){
    eid=0;
    memset(head,-1,sizeof(head));
}
//有向边 三个参数,无向边4个参数
void addEdg(int u,int v,captype c,captype rc=0){
    edg[eid].to=v; edg[eid].next=head[u];
    edg[eid].cap=c; edg[eid].flow=0; head[u]=eid++;

    edg[eid].to=u; edg[eid].next=head[v];
    edg[eid].cap=rc; edg[eid].flow=0; head[v]=eid++;
}
captype maxFlow_sap(int sNode,int eNode, int n){//n是包括源点和汇点的总点个数,这个一定要注意
    memset(gap,0,sizeof(gap));
    memset(dis,0,sizeof(dis));
    memcpy(cur,head,sizeof(head));
    pre[sNode] = -1;
    gap[0]=n;
    captype ans=0;  //最大流
    int u=sNode;
    while(dis[sNode]<n){   //判断从sNode点有没有流向下一个相邻的点
        if(u==eNode){   //找到一条可增流的路
            captype Min=INF ;
            int inser;
            for(int i=pre[u]; i!=-1; i=pre[edg[i^1].to])    //从这条可增流的路找到最多可增的流量Min
            if(Min>edg[i].cap-edg[i].flow){
                Min=edg[i].cap-edg[i].flow;
                inser=i;
            }
            for(int i=pre[u]; i!=-1; i=pre[edg[i^1].to]){
                edg[i].flow+=Min;
                edg[i^1].flow-=Min;  //可回流的边的流量
            }
            ans+=Min;
            u=edg[inser^1].to;
            continue;
        }
        bool flag = false;  //判断能否从u点出发可往相邻点流
        int v;
        for(int i=cur[u]; i!=-1; i=edg[i].next){
            v=edg[i].to;
            if(edg[i].cap-edg[i].flow>0 && dis[u]==dis[v]+1){
                flag=true;
                cur[u]=pre[v]=i;
                break;
            }
        }
        if(flag){
            u=v;
            continue;
        }
        //如果上面没有找到一个可流的相邻点,则改变出发点u的距离(也可认为是高度)为相邻可流点的最小距离+1
        int Mind= n;
        for(int i=head[u]; i!=-1; i=edg[i].next)
        if(edg[i].cap-edg[i].flow>0 && Mind>dis[edg[i].to]){
            Mind=dis[edg[i].to];
            cur[u]=i;
        }
        gap[dis[u]]--;
        if(gap[dis[u]]==0) return ans;  //当dis[u]这种距离的点没有了,也就不可能从源点出发找到一条增广流路径
                                        //因为汇点到当前点的距离只有一种,那么从源点到汇点必然经过当前点,然而当前点又没能找到可流向的点,那么必然断流
        dis[u]=Mind+1;//如果找到一个可流的相邻点,则距离为相邻点距离+1,如果找不到,则为n+1
        gap[dis[u]]++;
        if(u!=sNode) u=edg[pre[u]^1].to;  //退一条边
    }
    return ans;
}
int fath[MAXN];
int findfath(int x){
    if(x!=fath[x])
        fath[x]=findfath(fath[x]);
    return fath[x];
}
void linke(int x,int y){
    x=findfath(x);
    y=findfath(y);
    fath[x]=y;
}
int main()
{
    int T,_cas=0,n, p ,in[50],out[50];
    char s[50];
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        init();
        memset(in,0,sizeof(in));
        memset(out,0,sizeof(out));
        for(int i=0; i<=26; i++)
            fath[i]=i;

        int flag = 1 , root =0;
        for(int i=0; i<n; i++){
            scanf("%s%d",s,&p);
            int u=s[0]-'a';
            int v=s[strlen(s)-1]-'a';
            out[u]++; in[v]++;
            root=u;
            if(p==1) addEdg(u,v,1);    //双向的则建一条容量为1的边
            linke(u,v);
        }
        root=findfath(root);
        int cnt=0 , u=-1,v=-1;
        for(int i=0; i<26 ; i++)
        if(in[i]||out[i]){
            if(findfath(i)!=root){
                flag=0; break;
            }
            if((in[i]+out[i])&1){
                cnt++;
                if(u==-1)
                    u=i;
                else v=i;
            }
        }
        if(cnt!=0&&cnt!=2) flag=0;
        if(flag==0){
            printf("Case %d: Poor boy!\n",++_cas); continue;
        }
        if(cnt==2){
            out[u]++; in[v]++;
            addEdg(u,v,1);  //构造成欧拉环,添加的是双向边
        }
        int s=26 , t = 27;
        for(int i=0; i<26; i++){
            if(out[i]>in[i])
                addEdg(s,i,(out[i]-in[i])/2);
            else if(out[i]<in[i])
                addEdg(i,t,(in[i]-out[i])/2);
        }
        maxFlow_sap( s , t , t+1);
        for(int i=head[s]; i!=-1; i=edg[i].next)//判断满流
        if(edg[i].cap>0 && edg[i].cap>edg[i].flow){
            flag=0; break;
        }

        if(flag) printf("Case %d: Well done!\n",++_cas);
        else  printf("Case %d: Poor boy!\n",++_cas);
    }
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值