uva 10054 The Necklace

推荐技术公众号:不爱睡觉的大猪

题意:给你n个珠子,一个珠子分为两半有两种颜色,用1到50来表示50种不同的颜色。把这些珠子串起来,两个紧挨着的珠子要满足一个条件就是接触的那部分颜色要相同

例如(1,2)(2,4),两个珠子的接触部分颜色相同都为2。当然,因为珠子最后是连成环的,第一个珠子和最后一个珠子也会接触,也要买满足这个条件

先输入T,有T组数据

输入n,有n个珠子

下面n行每行两个数字表示这个珠子的两个颜色,然后问你能不能连成一条链,能的话输出任意一种连接情况即可,不能的话输出失败

其本质是欧拉回路,欧拉回路的题只做过每背景的裸题,第一次做这个,想不到是欧拉回路,然后想了差不多一个小时才想到是欧拉回路,说说思考的思路

其实1到50代表50钟颜色也就是50个点,一个珠子的信息例如(1,2)其实就是一个无向边(1,2),注意是无向边,因为珠子是可以转过来的,(1,2)=(2,1),那么无疑就可以建立一个图了

另外,给你的n个珠子如果是能连成链的话,那么其实任意一个珠子都可以作为起点,最后要回到自己,然后就想到,那不就是在已有的图中进行遍历,遍历了所有的点然后回到自己吗?怎么那么像那么欧拉的………………然后再思考了一些细节,稍微推理了一下确定了就是要判断是否存在欧拉回路,若存在即输出路径

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

1.判断所有的点的度是否为偶数,如果有点不为偶数,则不存在欧拉回路

2.满足1的条件下,判断所给的图是否连通,不连通也不是欧拉回路

3.满足1和2的就是欧拉回路,然后dfs逆序输出路径,主要一定要逆序,不逆序是错的,后面会解释

经过有意的测试那先这道题是不需要判断图连通的,也就是所给数据图一定是连通的,所以直接判断度即可

先给出代码,在详细分析

#include <stdio.h>
#include <string.h>
#define N 55
#define MAX 1010
int g[N][N],vis[N];
int d[N];
int n;


void euler(int u)
{
    int v;
    for(v=1; v<=50; v++) 
        if(g[u][v])
        {
            g[u][v]--;
            g[v][u]--;
            euler(v);
            printf("%d %d\n",v,u);
            //一定要逆序输出
        }
}
int main()
{
    int t,T;
    int i,j;
    int u,v;
    int count,max,start;
    scanf("%d",&T);
    for(t=1 ;t<=T; t++)
    {
        memset(g,0,sizeof(g));
        memset(vis,0,sizeof(vis));
        memset(d,0,sizeof(d));
        scanf("%d",&n);
        for(i=1 ;i<=n; i++)
        {
            scanf("%d%d",&u,&v);
            d[u]++;
            d[v]++;
            g[u][v]++;
            g[v][u]++;
        }
        printf("Case #%d\n",t);

        //图是连通的,要判断所有点的度是否有为偶数    
        max=0; start=0;
        for(i=1 ;i<=50; i++)
            if( d[i]%2 )
                break;
        if(i<=50)
            printf("some beads may be lost\n");
        else  //图连通而且所有点的度都为偶数,则是一个欧拉回路,输出路径
            for(i=1; i<=50; i++)
                euler(i);

        if(t!=T) printf("\n");
    }
    return 0;
}

整个问题容易WA的地反是输出路径,其实就是一个dfs

for(i=1; i<=50; i++)
    euler(i);


void euler(int u)
{
    int v;
    for(v=1; v<=50; v++) 
        if(g[u][v])
        {
            g[u][v]--;
            g[v][u]--;
            euler(v);
            printf("%d %d\n",v,u);
            //一定要逆序输出,而且注意输出的边是(v,u)而不是(u,v)
        }
}

 

如果写成这样是错的

void euler(int u)
{
    int v;
    for(v=1; v<=50; v++) 
        if(g[u][v])
        {
            g[u][v]--;
            g[v][u]--;
       printf("%d %d\n",u,v);

       euler(v);
    //这样相当于顺序输出
        }
}

在输入的时候使会有重边的,也就是g[i][j]的值不一定只是为1

然后从一个点出发,找到和他相连的点,然后删除这条无向边,所以是  g[u][v]--;   g[v][u]--;  然后就去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,同时进行压栈等一系列操作,最后,必定会回到该点,然后继续回溯,直到顶点,逆序输出,结束

 
 
原文地址:https://www.cnblogs.com/scau20110726/p/2762371.html