描述
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
#include <cstdio>
#include <iostream>
using namespace std;
const int MAX = 20005;
struct Node
{
int next,to;
};
int head[MAX];
int cnt;
int father[MAX];//父节点记录
int irank[MAX] = {0};//记录秩
//寻找最高父节点
int find(int x){
return father[x] == x?x:find(father[x]);//当节点等于自身时为最高父节点
}
//构建邻接表
void add(int x,int y,Node e[]){
cnt++;
e[cnt].next = head[x];
e[cnt].to = y;
head[x] = cnt;
}
//建立并查集
void stru(int x,int y) {
//寻找x、y的最高父节点
int SetX = find(x);
int SetY = find(y);
//当两个点的父节点不同时 进行合并
if(SetX != SetY){
//秩小的集合的最高父节点认秩大的集合的最高父节点做父节点
if(irank[SetX] > irank[SetY])father[SetY] = SetX;
else if(irank[SetX] < irank[SetY])father[SetX] = SetY;
else if(irank[SetX] == irank[SetY]){//两个最高父节点相同时 默认y归顺x
father[SetY] = SetX;
irank[SetX]++;//x的秩+1
}
}
}
//判断能否
bool judge(int NumOfPoint,Node e[]){
int setm = find(1);//setm记录i的前一个数的最高父节点
for(int i = 2;i <= NumOfPoint;i++){
int setn = find(i);
if(setn != setm)return false;
else setn = setm;//下一次目前的setm将成为setn
}
int sum = 0;//记录奇数度节点的度数
for(int i = 1;i <= NumOfPoint;i++){
int tempum = 0;
for(int j = head[i];j != 0;j = e[j].next){
tempum++;//记录每一个节点的度数
}
if(tempum % 2 == 1)sum++;
}
if(sum > 2 || sum == 1)return false;//奇数节点的数目不符合欧拉路径原理
return true;
}
int main(int argc, char const *argv[]) {
//N组数据
int N;
scanf("%d",&N);
while (N--) {
//点数 线数
int NumOfPoint,NumOfWire;
scanf("%d%d",&NumOfPoint,&NumOfWire);
//重置
Node e[MAX];
cnt = 0;
fill(head,head + MAX,0);
fill(irank,irank + MAX,0);
//开始父节点都等于自己
for(int i = 1;i < NumOfPoint;i++)father[i] = i;
//输入路径
for(int i = 1;i <= NumOfWire;i++){
int x,y;
scanf("%d%d",&x,&y);
//创建邻接表
add(x,y,e);
add(y,x,e);
stru(x,y);//创建并查集
}
if(judge(NumOfPoint,e))printf("Yes\n");
else printf("No\n");
}
return 0;
}