HS BDC 【HDU - 3472】【混合半欧拉图构建欧拉图+最大流】

15 篇文章 0 订阅
14 篇文章 0 订阅

题目链接


  有N个字符,如果字符可以首尾相同字符相接组成一条链的话,那么就是说明是well done的,不然,就不是,所以考虑成一条边,我们把每个字符串考虑成有向边,又有些字符串是可以反转的,实际上可以把它当成是无向边来考虑,现在,就是要知道,构成一个半欧拉图,也就是欧拉通路需要将这几条无向边怎样变化,或者不可能组成一个欧拉通路。

  其实,与混合欧拉图不同的混合半欧拉图,与其对应的欧拉图的差别就是在于出现了两个度可以为奇数的点。怎样处理这两个点,成为了本题的最重要的难点了。

  我们来看看欧拉图与半欧拉图之间的差别,再做定夺:

  1. 欧拉图没有奇数度的点,半欧拉图可以有一对(两个)奇数度的点
  2. 对于半欧拉图的两个始末点(也就是两个奇数度的点)有向链接就可以构成欧拉图

以第一条性质为基础的第二条性质,其实也就是解决这道题的关键之所在了,我们可以对这个不是欧拉图的半欧拉图,加一条边构建成欧拉图,而且,我们加入的这条边还是可以改变方向的,所以,我们把加入的这条边当成无向边来使。

  于是,问题回到了解决混合欧拉图的问题上来了,而解决混合欧拉图,我们已经有了先例。

  《混合欧拉图的求解思维方向》

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <bitset>
//#include <unordered_map>
//#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
//#define INF 10000007.
#define eps 1e-7
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
#define MP(a, b) make_pair(a, b)
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 30, maxM = 1e3 + 37;
int N, M, in_du[maxN], out_du[maxN], root[maxN];
int fid(int x) { return x == root[x] ? x : root[x] = fid(root[x]); }
int head[maxN], cnt, cur[maxN];
bool used[maxN];
struct Eddge
{
    int nex, to, flow;
    Eddge(int a=-1, int b=0, int c=0):nex(a), to(b), flow(c) {}
}edge[maxM << 1];
inline void addEddge(int u, int v, int w)
{
    edge[cnt] = Eddge(head[u], v, w);
    head[u] = cnt++;
}
inline void _add(int u, int v, int w) { addEddge(u, v, w); addEddge(v, u, 0); }
struct Max_Flow
{
    int S, T, n;
    int gap[maxN], d[maxN], que[maxN], ql, qr;
    inline void init()
    {
        n = T;
        for(int i=S; i<=T; i++)
        {
            gap[i] = d[i] = 0;
            cur[i] = head[i];
        }
        ++gap[d[T] = 1];
        que[ql = qr = 1] = T;
        while(ql <= qr)
        {
            int x = que[ql ++];
            for(int i=head[x], v; ~i; i=edge[i].nex)
            {
                v = edge[i].to;
                if(!d[v]) { ++gap[d[v] = d[x] + 1]; que[++qr] = v; }
            }
        }
    }
    inline int aug(int x, int FLOW)
    {
        if(x == T) return FLOW;
        int flow = 0;
        for(int &i=cur[x], v; ~i; i=edge[i].nex)
        {
            v = edge[i].to;
            if(d[x] == d[v] + 1)
            {
                ll tmp = aug(v, min(FLOW, edge[i].flow));
                flow += tmp; FLOW -= tmp; edge[i].flow -= tmp; edge[i ^ 1].flow += tmp;
                if(!FLOW) return flow;
            }
        }
        if(!(--gap[d[x]])) d[S] = n + 1;
        ++gap[++d[x]]; cur[x] = head[x];
        return flow;
    }
    inline int max_flow()
    {
        init();
        int ret = aug(S, INF);
        while(d[S] <= n) ret += aug(S, INF);
        return ret;
    }
} MF;
inline void init()
{
    cnt = 0; MF.S = 0; MF.T = 27;
    for(int i=0; i<=27; i++)
    {
        root[i] = i; head[i] = -1; used[i] = false;
        in_du[i] = out_du[i] = 0;
    }
}
char s[25];
int main()
{
    int T; scanf("%d", &T);
    for(int Cas=1; Cas <= T; Cas++)
    {
        init();
        scanf("%d", &N);
        for(int i=1, len, u, v, op, fu, fv; i<=N; i++)
        {
            scanf("%s%d", s + 1, &op);
            len = (int)strlen(s + 1);
            u = s[1] - 'a' + 1; v = s[len] - 'a' + 1;
            used[u] = used[v] = true;
            fu = fid(u); fv = fid(v);
            if(fu ^ fv) root[fu] = fv;
            out_du[u]++; in_du[v]++;
            if(u ^ v) _add(u, v, 1);
        }
        printf("Case %d: ", Cas);
        int root_num = 0, point_num = 0, p[3], sum = 0;
        bool ok = true;
        for(int i=1, x; i<=26; i++)
        {
            if(!used[i]) continue;
            root_num += fid(i) == i;
            if(root_num > 1) { ok = false; break; }
            if((in_du[i] + out_du[i]) & 1)
            {
                p[point_num ++] = i;
                if(point_num > 2) break;
            }
            else
            {
                if(in_du[i] == out_du[i]) continue;
                if(out_du[i] > in_du[i])
                {
                    x = (out_du[i] - in_du[i]) / 2;
                    _add(MF.S, i, x);
                }
                else
                {
                    x = (in_du[i] - out_du[i]) / 2;
                    _add(i, MF.T, x);
                }
                sum += x;
            }
        }
        if(point_num)   //如果是半欧拉图的情况下,自己构建欧拉图
        {
            out_du[p[0]] ++; in_du[p[1]] ++;
            _add(p[0], p[1], 1);
            for(int i=0, x; i<2; i++)
            {
                if(out_du[p[i]] == in_du[p[i]]) continue;
                if(out_du[p[i]] > in_du[p[i]])
                {
                    x = (out_du[p[i]] - in_du[p[i]]) / 2;
                    _add(MF.S, p[i], x);
                }
                else
                {
                    x = (in_du[p[i]] - out_du[p[i]]) / 2;
                    _add(p[i], MF.T, x);
                }
                sum += x;
            }
        }
        sum >>= 1;
        if(!ok || point_num > 2) { printf("Poor boy!\n"); continue; }
        if(MF.max_flow() == sum) printf("Well done!\n");
        else printf("Poor boy!\n");
    }
    return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wuliwuliii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值