UVa10054

题目链接

分析:
一开始我把每种颜色拆成两个点(下面都称为第一个点,第二个点)
如果一个珠子是左x右y
那就连接x2—>y1,规定这种边只能走一次
每种颜色都连接x2—>x1,规定这种边可以走无数次

这样建好图之后,从任一颜色的第一个点开始遍历
看能不能到达这一颜色的第二个点并且所有只能走一次的边都经过了

这样确实是可以解决这个问题
但是有些边可以经过无数次,在实际判断的时候有点困难

实际上是没有必要拆点的,
我们把每种颜色视为一个点,每个珠子的两半连一条无向边
则题目就转化成了欧拉回路问题

欧拉回路

若图G中存在这样一条路径,使得它恰通过G中每条边一次,则称该路径为欧拉路径。若该路径是一个圈,则称为欧拉(Euler)回路。

无向图的欧拉回路判断和路径输出:

1.判断所有的点的度是否为偶数,如果有点不为偶数,则不存在欧拉回路
2.满足1的条件下,判断所给的图是否连通,不连通也不是欧拉回路
3.满足1和2的就是欧拉回路,然后dfs逆序输出路径,主要一定要逆序

这道题不用判断是否连通
(题目比较良心,给出的图都是合法的)

Q.什么叫逆序输出?
A.

for (int i=1;i<=50;i++)
    if (mp[now][i])
    {
        mp[now][i]--;
        mp[i][now]--;
        euler(i);
        printf("%d %d
",i,now);
        //一定要逆序输出 
    }

Q.顺序输出是什么样呢
A.

printf("%d %d
",now,i);
euler(i);

Q.为什么不能顺序输出呢
A.
在输入的时候会有重边,也就是说mp[i][j]的值不一定只是为1
我们从一个点出发,找到和他相连的点,然后删除这条无向边,

mp[now][i]--;
mp[i][now]--;

然后就去dfs下一个点v,最后在递归返回的时候才输出路径
之所以逆序输出
是因为和当前点i相连的点可能不止一个

例如当前点是1,上一条边是(3,1) .
而和1相连的点有2,7,11,能分成3个方向

往2的方向有 : (1,2)(2,4)
往7的方向有 : (1,7)(7,5)(5,6)
往11的方向有 : (1,11)(11,12)(12,13)

如果顺序输出将会是
3 1
1 2
2 4
1 7
7 5
5 6
1 11
11 12
12 13

计算机中,上述过程是这样的

从起点开始,将起点压入栈中,
然后访问与顶点相连的一个顶点,将该顶点压入栈中,同时删除这条边,
继续DFS寻找顶点,并同样压栈、删除,
最后,直到走到一个没有任何边与它相连的顶点(可能是起始点,也可能不是),
便开始进行回溯,
回溯的同时进行弹栈,弹栈的结果也就是欧拉回路的逆序输出结果
回溯的过程就是寻找相连路径的过程,
如果回溯的过程中发现仍然有边与当前顶点相连,
那么继续从这个顶点沿着未删除的边去DFS,同时进行压栈等一系列操作,
最后,必定会回到该点

相反的,如果正向输出,
可能会出现断掉的现象(从一个点突然跳到另一个点)

tip

点表示的是颜色
所以点的循环都是

1~50

要了解算法的内部实现
这样有助于避免一些玄学的错误

图论比数论难搞多了,就连bfs都有可能写错

可见,
不光要学会数学建模,
对于模型的简化也是很重要的

//这里写代码片
#include<cstdio>
#include<cstring>
#include<iostream>

using namespace std;

int n;
const int N=55;
int mp[N][N],in[N];

void euler(int now)
{
    for (int i=1;i<=50;i++)
        if (mp[now][i])
        {
            mp[now][i]--;
            mp[i][now]--;
            euler(i);
            printf("%d %d
",i,now);
            //一定要逆序输出 
        }
}

int main()
{
    int T;
    scanf("%d",&T);
    for (int TT=1;TT<=T;TT++)
    {
        memset(mp,0,sizeof(mp));
        memset(in,0,sizeof(in));

        scanf("%d",&n);
        int s;
        for (int i=1;i<=n;i++)
        {
            int u,w;
            scanf("%d%d",&u,&w);
            if (i==1) s=u;
            mp[u][w]++; mp[w][u]++;
            in[w]++;
            in[u]++;
        }

        printf("Case #%d
",TT);
        int ff=1;
        for (int i=1;i<=50;i++)    //点代表的是颜色,我们要判断所有的点 
            if (in[i]&1)
            {
                ff=0;
                break;
            }

        if (!ff)
           printf("some beads may be lost
");
        else
        {
            for (int i=1;i<=50;i++)
                euler(i); 
        } 

        if (TT!=T) puts("");
    }
    return 0;
}
原文地址:https://www.cnblogs.com/wutongtong3117/p/7673013.html