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,同时进行压栈等一系列操作,最后,必定会回到该点,然后继续回溯,直到顶点,逆序输出,结束

 
 

你可能感兴趣的:(uva)