HDU 3472 HS BDC 混合图的欧拉路径判断 (网络流)

HDU 3472 HS BDC 混合图的欧拉路径判断

题意:给n个字符串,这些字符串如果某个字符串的尾部字母和另一个字符串的首部字母相等,那么这两个字母就可以连起来,有的字符串可以反转,问是否存在一种情况使得所有字符串都连成一条链。

思路:
字符串的首字母–>字符串的尾字母建一条有向边,如果字符串是可反转的,那么就建无向边,
那么问题就转换为:
是否存在一种无向边定向方案,使得这个图中存在这么一条欧拉路径。
给欧拉路径的终点->起点(度数为奇的点)加条边,边权为1,使之成为欧拉回路。
首先任意定向无向边为有向边。建图的时候,若为无向边则加边,方向任意定,边权为1。否则不加边,因为有向边已经不能改变,然后记录每个节点的出度和入度的差。
设差为k,k如果为偶数的话,那么如果我改变k/2条与这个节点相关的边就可以使得这个节点的 出度=入度。
k如果为奇数的话,假设有两个节点的k为奇数那么其中一个是起点另一个是终点。从终点到起点建一条边,边权为1
如果差为k>0,那么说明 出度>入度,需要减少出度增加入度来使得 出度=入度,超级源点->该点,边权为k/2
如果差为k<0,那么说明 出度<入度,需要增加出度减少入度来使得 出度=入度,该点->超级汇点,边权为-k/2
如果差为k=0,那么说明 出度=入度。
如此建图之后判满流,如果满流则说明存在欧拉回路,是欧拉图。

#include <iostream>  
#include <cstdio>  
#include <cstring>  
#include <cmath>  
#include <queue>  
#include <set>  
#include <algorithm>  
#include <limits.h>  
#define LL long long
#define N 1010  
#define M 10010  
#define inf 10000000
using namespace std;  

const int INF = INT_MAX;  

struct Edge{  
    int from, to, w, nxt;  
}ed[M << 2]; 

int head[N], idc = 1, S, T;  
int dis[N];  
int top, sta[N], last[N];  

void init(){  
    memset(head, 0, sizeof(head));  
    idc = 1;  
}  

void adde(int u, int v, int w){  
    ed[++idc].from = u;
    ed[idc].to = v;
    ed[idc].w = w;
    ed[idc].nxt = head[u];  
    head[u] = idc;  
}  

bool bfs(){
    memset(dis, -1, sizeof(dis));
    queue <int> q; q.push(S); dis[S]=0;  
    while( !q.empty() ){
        int u = q.front(); q.pop();    
        for(int k=head[u]; k; k=ed[k].nxt) {
            int v = ed[k].to;
            if(ed[k].w && dis[v] == -1){  
                dis[v] = dis[u] + 1;
                q.push( v );  
                if(v == T) break;
            }
        }
    }
    memcpy(last, head, sizeof(head));  
    return dis[T] != -1; 
}

int find(int u, int low){
    if (u==T || low==0) return low;  
    int totflow =0;
    for(int k=last[u]; k; last[u]=k=ed[k].nxt){
        int v = ed[k].to;
        if(ed[k].w && dis[v] == dis[u] + 1){
            int f = find(v, min(low,ed[k].w));  
            ed[k].w -= f; ed[k^1].w += f;  
            totflow += f; low -= f;
            if(low == 0) return totflow;
        }
    }
    if( !totflow ) dis[u] = -1;
    return totflow;  
}  

LL Dinic(){
    LL ans = 0;
    while (bfs()) ans += find(S, INF);
    return ans;
}  

int du[30];  
int fa[30], use[30];  

int findfa(int x){  
    return x==fa[x] ? x : fa[x] = findfa(fa[x]);  
}  

int Union(int x, int y){  
    x = findfa(x), y = findfa(y);  
    if(x == y) return false;  
    fa[x] = y;  
    return true;  
}

int main(){
    int t, n, cas=0;  
    char ch[30];  
    scanf("%d", &t);  
    while( t-- ){  
        scanf("%d", &n);  
        init();  
        for(int i=0; i<26; i++){  
            fa[i] = i; use[i] = 0; du[i] = 0;  
        }  
        for(int i=0; i<n; i++){  
            int x; scanf("%s%d", ch, &x);  
            int st = ch[0] - 'a';  
            int ed = ch[strlen(ch) - 1] - 'a';  
            du[st]++; du[ed]--;  
            use[st] = 1; use[ed] = 1;  
            if( x ) adde(st, ed, 1), adde(ed, st, 0);  
            Union(st, ed);  
        }  
        int num1 = 0, num2 = 0;
        S = -1, T = -1;  
        for(int i=0; i<26; i++)  
            if( use[i] ){  
                if(fa[i] == i) ++num1;//连通块个数 
                if(du[i] % 2){  
                    ++num2;//奇度数的点的个数(作为欧拉路径的起始点,终止点) 
                    if(du[i] < 0) S = i;  
                    if(du[i] > 0) T = i;  
                }  
            }
        printf("Case %d: ", ++cas);  
        if(num1!=1 || num2==1 || num2>2 || (num2==2 && (S==-1 || T==-1))){  
            puts("Poor boy!"); continue;  
        }              
        if(num2 == 2) adde(T, S, 1), adde(S, T, 0);  
        S = 26, T = 27;             
        LL sum = 0;  
        for(int i=0; i<26; i++)  //流量为1,度数改变2 
            if(du[i] > 0) adde(S, i, du[i] >> 1), adde(i, S, 0), sum += du[i] >> 1;  
            else if(du[i] < 0) adde(i, T, -du[i] >> 1), adde(T, i, 0);  
        if(Dinic() != sum) puts("Poor boy!");  
        else puts("Well done!");  
    }  
    return 0;  
}  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值