一笔画问题
时间限制:
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
-
第一行只有一个正整数N(N<=10)表示测试数据的组数。
这题搞了好久,也WA了好几次,刚开始用dfs直接做,样列过了,但有错误,WA了。 然后呢想到每个点可以走多次,两个点之间有多条路,但仅且只能走一次,赤裸裸的欧拉通路了。就当再次回顾欧拉吧! (刚开始还纠结可能有些孤立的点,无法用欧拉来做,好吧确实想多了!!)
分析:欧拉道路(通路)的判断:
1、必须满足连通性。(这里我用dfs求,也可用并查集做)
2、奇度顶点个数为0个或两个。(对于欧拉回路来说,奇度顶点的个数只能是0个)
AC代码:
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int maxn=1000+5;
vector<int>g[maxn];
int vis[maxn];
int P,Q;
int check(){
int tol=0;
for(int i=0;i<P;i++){
int tmp=g[i].size();
if(tmp%2!=0)tol++; //记录奇度定点的个数
}
return tol;
}
int cur;
void dfs(int i){
for(int j=0;j<g[i].size();j++){
if(!vis[g[i][j]]){
vis[g[i][j]]=1;
cur++;
dfs(g[i][j]);
}
}
}
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&P,&Q);
memset(vis,0,sizeof(vis));
for(int i=0;i<Q;i++){
int x,y;
scanf("%d%d",&x,&y);
g[x].push_back(y);
g[y].push_back(x);
}
int tol=check();
if(tol==2 || tol==0)
{
vis[1]=1; //标识是否被访问
cur=1; //统计联通点的个数
dfs(1);
if(cur==P)printf("Yes\n");
else printf("No\n");
}
else printf("No\n");
for(int i=1;i<=P;i++)g[i].clear();
}
return 0;
}
解法二:并查集
AC代码:
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1000+10;
int u[maxn],v[maxn]; //(u,v)表示边,u,v表示边的两点
int pre[maxn];
int tol[maxn];
int p,q; //点的个数,边的条数
int find(int x){ //查找
return x==pre[x]? x : find(pre[x]);
}
void Union(int x,int y){ //合并
int fx=find(x),fy=find(y);
if(fx!=fy){
pre[fx]=fy;
}
}
int main(){
int N;
scanf("%d",&N);
while(N--){
memset(tol,0,sizeof(tol));
scanf("%d%d",&p,&q);
for(int i=0;i<q;i++){
int a,b;
scanf("%d%d",&a,&b);
u[i]=a,v[i]=b;
tol[a]++;
tol[b]++;
}
int cnt=0;
for(int i=1;i<=p;i++){ //统计奇度定点个数
if(tol[i]%2!=0)cnt++;
}
if(cnt==0 || cnt==2){
for(int i=1;i<=p;i++)pre[i]=i; //初始化
for(int i=0;i<q;i++) //合并
Union(u[i],v[i]);
memset(tol,0,sizeof(tol));
for(int i=1;i<=p;i++)
tol[find(i)]++;
cnt=0;
for(int i=1;i<=p;i++) //统计连通块个数
if(tol[i])cnt++;
if(cnt>1)printf("No\n"); //个数只能是1
else printf("Yes\n");
}
else printf("No\n");
}
return 0;
}