UVA 10054 欧拉回路

题意:给n个珠子,每个珠子有2种颜色,把所有珠子连成一个环,连接规则是2个珠子的连接处颜色必须相同,每种颜色用数字表示


理解:直接联想哈密顿回路,不科学,写不来。联想之前做的单词那个题(点击打开链接,这个讲的比较详细),然后果断欧拉回路,只不过那个是有向图,这个是无向图,并且输出路径。


方便看我还是贴出来吧



首先是几个概念:

欧拉回路 图中经过每条边一次并且仅一次的回路称作欧拉回路。

欧拉路径 图中经过每条边一次并且仅一次的路径称作欧拉路径。

欧拉图 存在欧拉回路的图称为欧拉图。

半欧拉图 存在欧拉路径但不存在欧拉回路的图称为半欧拉图。


然后就是判定

不管是这四种的哪几种,大前提是这个图必须是一个 连通图,否则什么都不是
满足前提过后;
(无向图)所有点的度数为偶数就是欧拉图
如果有2个点的度数为奇数就是半欧拉图


(有向图)所有点的入度==出度就是欧拉图

且存在顶点的入度比出度大1(终点)、的入度比出度小1(起点),其它所有顶点的入度等于出度。





怎么建图和判断欧拉回路都写在那个链接里面的。这次主要讲路径的输出。


后面写的所有内容都是在这个图(无向图)存在欧拉回路这个大前提下进行的

在网上看了些别人题解里面对于路径输出的题解,都是dfs遍历完所有边然后逆序存路径并输出。


一直想不通为什么要逆序,看了别人用PPT模拟了一个路径(点击打开链接),然后另外用其他点也进行了模拟下,就懂了


对一个点进行dfs的时候,假如这个点的度为2,那么从这个点出发必定会回到这个点(这个点即是起点也是终点,所以一边为起始边,另一边为终点边),如果回到这个点但是图中还有很多边,那么这条边就不能算最后一条边,必须把其他边都遍历完才能将这条边算作最后一条边。这样就证明正向dfs是错误的。


那为什么逆序是对的,dfs到一个点,这个点的度数减少为0,之前肯定也从这个点发出过一条边(大前提已说明有欧拉回路),那么从这条边也一定能回到之前的那条路,这样反过来把这一条边当成出发的第一条边反着走回去,一定能有序的回到之前的那条边。打个比方,就是你走路,往前走可以选择各个方向,那么倒着走之前走过的路是不是就是固定的路线了,且一定能回到起点。


然后这道题做法就是首先判断图是否连通,再判断是否存在欧拉回路,最后就是输出路径的问题了。


CODE

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>

using namespace std;

const int N = 55;

int n;
int E[N][N];    ///存边
int fa[N];      ///并查集判连通
int du[N];      ///每个点的度
bool vis[N];    ///标记这个点是否被访问

void Init()     ///初始化
{
    for(int i = 0;i < N;i++)
    {
        fa[i] = i;
        du[i] = 0;
        vis[i] = false;
        for(int j = 0;j < N;j++)
        {
            E[i][j] = 0;
        }
    }
}

int Find(int x)
{
    if(fa[x] != x)
        return fa[x] = Find(fa[x]);
    return fa[x];
}

void Union(int x,int y)
{
    int tx = Find(x);
    int ty = Find(y);
    fa[ty] = tx;
}

void dfs(int st)          ///dfs找路径
{
    for(int i = 1;i <= 50;i++)
    {
        if(E[st][i])
        {
            E[st][i]--;
            E[i][st]--;
            dfs(i);       ///先走再输出
            printf("%d %d\n",i,st);
        }
    }
}

int main(void)
{
    int T;
    scanf("%d",&T);
    int cas = 1;
    while(T--)
    {
        Init();
        scanf("%d",&n);
        for(int i = 1;i <= n;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            E[a][b]++;
            E[b][a]++;
            du[a]++;
            du[b]++;
            vis[a] = true;
            vis[b] = true;
            Union(a,b);      ///无向图必须这样写成Union
        }
        int cnt_root = 0;    ///根节点个数
        for(int i = 1;i <= 50;i++)
        {
            if(vis[i] && fa[i] == i)
                cnt_root++;
        }
        printf("Case #%d\n",cas++);
        if(cnt_root > 1)     ///用来作根节点的个数有1个以上说明不是连通图
        {
            printf("some beads may be lost\n");
            continue;
        }
        int flag = false;
        for(int i = 1;i <= 50;i++)
        {
            if(vis[i] && du[i]%2 == 1)   ///点的度数为奇数说明不是欧拉回路
                flag = true;
        }
        if(flag)
        {
            printf("some beads may be lost\n");
        }
        else
        {
            for(int i = 1;i <= 50;i++)
            {
                if(vis[i])
                {
                    dfs(i);
                    break;
                }
            }
        }
        if(T > 0) puts("");   ///TM这里没写返回WA你敢信
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值