HDU 3472 网络流

点击打开链接

题意:给n个串,是0则不可以反转,是1可以反转,问能否头尾相连的将所有串连在一起

思路:做法是先判断一下图是否联通,用并查集判断就行,然后是先将图变成欧拉回路,那么只能有两种情况,没有奇度顶点,或者有两个奇度顶点,一个cnt大于0,一个cnt小于0,cnt代表的是一个点的入度减去出度的值,只有这两种情况才可以形成一个欧拉回路,但是如果是两个奇度顶点的话,需要将大于0的顶点连向小于0的顶点,这样就可以变成一个欧拉回路了,然后处理的是图中的无向边,先将无向边随意转化成有向边,如1-->4那么网络流中建图建一条4-->1权值为2的边,一会说为什么权值为2,然后处理的是对于这些入度与出度不平衡的点,写着感觉与有上下界的网络流特别向,就套用了过来,如果cnt大于0,则源点与其相连权值为cnt,然后cnt小于0的与汇点相连,权值为-cnt,因为是这么连的,解释一下上边的权值为2的原因,1-->4 代表的1是出度,4是入度,而网络流建的图是4-->1代表的是4为出度,1为入度,两相比较的话是少了2,而不是1,太抽象了~~,看代码把

#include <queue>
#include <vector>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3fll;
const int maxn=2010;
struct edge{
    int to,cap,rev;
    edge(int a,int b,int c){to=a;cap=b;rev=c;}
};
vector<edge>G[maxn];
int level[maxn],iter[maxn];
void add_edge(int from,int to,int cap){
    G[from].push_back(edge(to,cap,G[to].size()));
    G[to].push_back(edge(from,0,G[from].size()-1));
}
void bfs(int s){
    memset(level,-1,sizeof(level));
    queue<int>que;level[s]=0;
    que.push(s);
    while(!que.empty()){
        int v=que.front();que.pop();
        for(unsigned int i=0;i<G[v].size();i++){
            edge &e=G[v][i];
            if(e.cap>0&&level[e.to]<0){
                level[e.to]=level[v]+1;
                que.push(e.to);
            }
        }
    }
}
int dfs(int v,int t,int f){
    if(v==t) return f;
    for(int &i=iter[v];i<G[v].size();i++){
        edge &e=G[v][i];
        if(e.cap>0&&level[v]<level[e.to]){
            int d=dfs(e.to,t,min(f,e.cap));
            if(d>0){
                e.cap-=d;
                G[e.to][e.rev].cap+=d;
                return d;
            }
        }
    }
    return 0;
}
int max_flow(int s,int t){
    int flow=0;
    while(1){
        bfs(s);
        if(level[t]<0) return flow;
        memset(iter,0,sizeof(iter));
        int f;
        while((f=dfs(s,t,inf))>0) flow+=f;
    }
}
int f[30],vis[30],cnt[30];//cnt代表入度减去出度的值
char str[maxn][50];
int find1(int x){
    if(x!=f[x]) f[x]=find1(f[x]);
    return f[x];
}
void unite(int a,int b){
    int aa=find1(a);
    int bb=find1(b);
    if(aa==bb) return ;
    f[aa]=bb;
}
int main(){
    int T,cas=1,n,type;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=0;i<maxn;i++) G[i].clear();
        for(int i=0;i<30;i++) vis[i]=0,f[i]=i,cnt[i]=0;
        for(int i=0;i<n;i++){
            scanf("%s%d",str[i],&type);
            int a=str[i][0]-'a'+1;
            int len=strlen(str[i]);
            int b=str[i][len-1]-'a'+1;
            cnt[a]--;cnt[b]++;
            vis[a]=1;vis[b]=1;
            if(type) add_edge(b,a,2);
            unite(a,b);
        }
        int sum=0;
        for(int i=1;i<=26;i++) if(vis[i]&&f[i]==i) sum++;
        printf("Case %d: ",cas++);
        if(sum>1){
            printf("Poor boy!\n");continue;
        }
        int sum1=0,t1,t2,sum2=0;
        for(int i=1;i<=26;i++){
            if(cnt[i]>0&&cnt[i]%2==1){
                sum1++;t1=i;
            }else if(cnt[i]<0&&-cnt[i]%2==1){
                sum2++;t2=i;
            }
        }
        if(sum1==1&&sum2==1) add_edge(t1,t2,1);
        else if(sum1==0&&sum2==0){}
        else{
            printf("Poor boy!\n");continue;
        }
        sum=0;
        for(int i=1;i<=26;i++){
            if(cnt[i]>0){
                sum+=cnt[i];add_edge(0,i,cnt[i]);
            }else if(cnt[i]<0){
                add_edge(i,27,-cnt[i]);
            }
        }
        int ans=max_flow(0,27);
        if(sum==ans) printf("Well done!\n");
        else printf("Poor boy!\n");
    }
    return 0;
}

你可能感兴趣的:(ACM,图论,HDU,网络流)