【并查集 欧拉路径】hdu 1116 Play on Words

表示我是某天搜并查集的时候看到 这位大神的代码,然后去做了这题的。

题意:判断n个单词是否可以相连成一条链或一个环,两个单词可以相连的条件是 前一个单词的最后一个字母和后一个单词的第一个字母一样。取每个单词的收尾字母,想象成两个节点,这两个节点之间有个路径。这样就构成了一个图。

这题里,并查集只是一个辅助,用来判断图是不是连通的。真正用来判断的是欧拉回路。

定义:
若图G中存在这样一条路径,使得它恰通过G中每条边一次,则称该路径为欧拉路径。若该路径是一个圈,则称为欧拉(Euler)回路。具有欧拉路径的图称为欧拉图(简称E图)。半欧拉图:没有欧拉环,但有至少一条欧拉路径的图。
判断:

以下判断基于此图的基图连通。

无向图存在欧拉回路的充要条件:一个无向图存在欧拉回路,当且仅当该图拥有奇数度数的顶点的个数为0且该图是连通图。

无向图是半欧拉图的充要条件:当且仅当该图是连通的且有且只有2个点的度数是奇数(此时这两个点只能作为欧拉路径的起点和终点)

有向图存在欧拉回路的充要条件:所有顶点的入度等于出度且该图是连通图,或者 一个顶点的度数(入度-出度)为1,另一个度数为-1,其他顶点的度数为0。

有向图是半欧拉图的充要条件:当且仅当该图的基图(将所有有向边变为无向边后形成的无向图)是连通的且有且只有一个点的入度比出度少1(作为欧拉路径的起点),有且只有一个点的入度比出度多1(作为终点),其余点的入度等于出度。

混合图存在欧拉回路条件

要判断一个混合图G(V,E)(既有有向边又有无向边)是欧拉图,方法如下:

假设有一张图有向图G',在不论方向的情况下它与G同构。并且G'包含了G的所有有向边。那么如果存在一个图G'使得G'存在欧拉回路,那么G就存在欧拉回路。其思路就将混合图转换成有向图判断。实现的时候,我们使用网络流的模型。现任意构造一个G'。用Ii表示第i个点的入度,Oi表示第i个点的出度。如果存在一个点k,|Ok-Ik|mod 2=1,那么G不存在欧拉回路。接下来则对于所有Ii>Oi的点从源点连到i一条容量为(Ii-Oi)/2的边,对于所有Ii<Oi的点从i连到汇点一条容量为(Oi-Ii)/2的边。如果对于节点U和V,无向边(U,V)∈E,那么U和V之间互相建立容量为无限大的边。如果此网络的最大流等于∑|Ii-Oi|/2,那么就存在欧拉回路。

#include <stdio.h>
#include <map>
#include <algorithm>
#include <string>
#include <string.h>
#include <iostream>
#define mm(a) memset(a, 0, sizeof(a))
using namespace std;

int father[26], vis[26];
int in[26], out[26];

int find_father(int x)
{
    if(father[x] != x) father[x] = find_father(father[x]);
    return father[x];
}

int main()
{
    int T, n;
    char s[1100];
    scanf("%d", &T);
    while(T--)
    {
        mm(in);
        mm(out);
        mm(vis);
        for(int i = 0; i < 26; i ++)
            father[i] = i;
        scanf("%d", &n);
        getchar();
        for(int i = 0; i < n; i ++)
        {
            gets(s);
            int len = strlen(s);
            int u = s[0] - 'a', v = s[len - 1] - 'a';
            vis[u] = vis[v] = 1;
            out[u]++, in[v]++;
            u = find_father(u), v = find_father(v);
            if(u != v) father[u] = v;
        }
        int point = 0;
        for(int i = 0; i < 26; i++)
        {
            if(vis[i] && father[i] == i)
                point++;
        }
        if(point > 1)
        {
            puts("The door cannot be opened.");
            continue;
        }
        int y = 0, z = 0, t = 0;
        for(int i = 0; i < 26; i++)
        {
            if(vis[i] && in[i] != out[i])
            {
                if(in[i] == out[i] + 1) y++;
                else if(in[i] + 1 == out[i]) z++;
                else t++;
            }
        }
        if(t)
            puts("The door cannot be opened.");
        else if((y == 1 && z == 1) || (z == 0 && y == 0))
            puts("Ordering is possible.");
        else
            puts("The door cannot be opened.");
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值