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