一笔画问题
时间限制:
3000 ms | 内存限制:
65535 KB
难度:
4
-
描述
-
zyc从小就比较喜欢玩一些小游戏,其中就包括画一笔画,他想请你帮他写一个程序,判断一个图是否能够用一笔画下来。
规定,所有的边都只能画一次,不能重复画。
-
输入
-
第一行只有一个正整数N(N<=10)表示测试数据的组数。
每组测试数据的第一行有两个正整数P,Q(P<=1000,Q<=2000),分别表示这个画中有多少个顶点和多少条连线。(点的编号从1到P)
随后的Q行,每行有两个正整数A,B(0<A,B<P),表示编号为A和B的两点之间有连线。
输出
-
如果存在符合条件的连线,则输出"Yes",
如果不存在符合条件的连线,输出"No"。
样例输入
-
2 4 3 1 2 1 3 1 4 4 5 1 2 2 3 1 3 1 4 3 4
样例输出
-
No Yes
-
一笔成画:就是说存在欧拉回路或者存在欧拉通路(涉及到离散数学),那么存在欧拉回路的条件:G是联通图且无奇度顶点!存在欧拉通路:G是连通图却恰好有两个奇度顶点!
-
所以首先判断是否联通!
-
再连通图的基础上,判断有没有奇度顶点,有几个!
-
两种方法:
-
1、深搜(时间效率低)
-
#include<stdio.h> #include<string.h> char map[1005][1005],vis[1005]; int bian[1005]; int p,q; void dfs(int e) { vis[e]=true; //这个点已经判断过了 for(int i=1;i<=p;i++) { if(map[e][i]) { bian[e]++; //这个点的边数 if(!vis[i]) //如果 第I个点没有走过的话,那就深搜I dfs(i); } } } int main() { int T; scanf("%d",&T); while(T--) { int biaoji=1; memset(map,false,sizeof(map)); memset(vis,false,sizeof(vis)); memset(bian,0,sizeof(bian)); int n,m; scanf("%d%d",&p,&q); for(int i=1;i<=q;i++) { scanf("%d%d",&n,&m); map[n][m]=true; map[m][n]=true; } dfs(1); for(int i=1;i<=p;i++) //判断是否联通 { if(!vis[i]) { biaoji=0; break; } } if(!biaoji) //有的点走不到,表示不连通,那么就直接出结果了 printf("No\n"); else //如果联通的话,那么判断有几个奇度顶点 { int oo=0; for(int i=1;i<=p;i++) { if(bian[i]%2) oo++; } if(oo==0 || oo==2) //有0或2个的话,那就说明是欧拉图 printf("Yes\n"); else printf("No\n"); } } return 0; }
-
2.并查集(高速)
-
并查集的思想真的很高效,将图的问题想象成拉帮结派,将所有连通的点分成一个帮派,树立一个掌门人!
-
最开始,一个图有几个点,就有几个门派,然后通过题中数据将有关系的点进行吞并,两派合成一派,只有一个掌门人
-
但是在吞并过程中,类似传销一样,一个人只知道自己的上线(father数组)是谁,并不知道自己的头头是谁,所以找头头只能一级一级的往上找(find函数),若头头一样,那就是一个帮派的
-
#include<stdio.h> #include<string.h> int father[1010],ans[1010]; void init(int e) { for(int i=0;i<=e;i++) //在最开始,每个点都是独立的 ,自己是自己的掌门人 father[i]=i; } int find(int e) { if(e==father[e]) //e是e的掌门人 return e; else //e 不是掌门人,只是一个下线,那么需要通过递归的方法进行查找,直到找到掌门人,然后返回掌门人 return father[e]=find(father[e]); } int main() { int T; scanf("%d",&T); while(T--) { memset(ans,0,sizeof(ans)); int n,m,x,y,count=0,amount=0; //count 代表有几个帮派,amount代表有几个奇度顶点 scanf("%d%d",&n,&m); init(n); for(int i=1;i<=m;i++) { scanf("%d%d",&x,&y); ans[x]++; ans[y]++; if(find(x)!=find(y)) //如果不是同一派的,那么进行拉帮结派,构成一派 father[x]=father[y]; } for(int i=1;i<=n;i++) if(find(i)==i) count++; //如果count等于1,那么就代表只有一个门派,即这个图的所有点是联通的,是连通图 for(int i=1;i<=n;i++) { if(ans[i]%2==1) amount++; //判断有几个奇度顶点 } if((amount==0 || amount==2) && count==1) //如果所有点都是联通的且只有2或0个奇度顶点,那么是欧拉图,可以一笔成画 printf("Yes\n"); else printf("No\n"); } return 0; }
-
这个网址是并查集的详解!很好,良心推荐!http://blog.youkuaiyun.com/dellaserss/article/details/7724401
-
可能是我离开这个工作室的最后的为数不多的文章,为以后留个念想!
-
第一行只有一个正整数N(N<=10)表示测试数据的组数。