nyist-42一笔画问题(欧拉通路) bfs||dfs||并查集

来源:互联网 发布:java多线程机制 编辑:程序博客网 时间:2024/04/30 22:02


一笔画问题

时间限制: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"。
样例输入
24 31 21 31 44 51 22 31 31 43 4
样例输出
NoYes

注:此题和hdu1878一笔画问题 略有差别,本题中不要求回到源点,因此判断是否为欧拉同路时,有以下规律:

  ■⒈凡是由偶点组成的连通图,一定可以一笔画成。画时可以把任一偶点为起点,最后一定能以这个点为终点画完此图。
  ■⒉凡是只有两个奇点的连通图(其余都为偶点),一定可以一笔画成。画时必须把一个奇点为起点,另一个奇点终点。
  ■⒊其他情况的图都不能一笔画出。(有偶数个奇点除以二便可算出此图需几笔画成。)
根据欧拉总结的规律,我们只需要1、判断图是否联通2、判断点是奇点的个数,就可以了。

方法一,利用判断图中顶点度数是否全部为偶数 或者 存在两个奇数顶点,同时利用 并查集 判断所有顶点是否联通

#include<iostream>#include<cstring>#include<queue>#include<algorithm>#include<cstdlib>#include<cstdio>using namespace std;#define INF 0x3f3f3f3fconst int MaxV = 1005;int degree[MaxV],fa[MaxV];//分别表示顶点的度数和顶点的父节点,就是指向该点的顶点int N,M;int find(int x)//获取根节点{    return fa[x]==x?x:find(fa[x]);}int main(){    int n ;    scanf("%d",&n);    while(n--){        scanf("%d%d",&N,&M);        memset(degree,0,sizeof(degree));//初始所有节点的度为0        for(int i = 1 ; i <= N; i++)            fa[i] = i ;                 //所有结点的父节点都是自身        for(int i = 0 ; i < M ; i ++){            int u,v;            scanf("%d%d",&u,&v);            degree[u]++;                 //无向图,出度+1            degree[v]++;                   //入度+1            int x = find(fa[u]);            int y = find(fa[v]);            if(x!=y) //判断输入的两个顶点是否属于同一个树,即是否联通                fa[x]=y;       //如果不联通,将其联通        }        bool t1=false,t2=true ;        int cnt = 0,cnt2=0 ;   //cnt2用来记录奇数点个数        for(int i = 1 ; i <= N ; i++){            if(fa[i] == i )     //判断所有顶点是否联通,若联通说明每个顶点的父节点都为某个顶点                cnt++;        //也就是说所有顶点中只有一个顶点与父节点相同            if(degree[i]&1){    //判断每个顶点的度数是否为奇数,一旦是,则证明不是欧拉图                cnt2++;                if(cnt2>=3)break;            }        }        if(cnt2!=2)    //只有当图中仅有2个奇数点,才为欧拉图            t2 = false ;        if (cnt == 1)            t1= true;        if(t1 && t2 )printf("Yes\n");//两个条件同时成立说明为欧拉图        else printf("No\n");    }return 0;}

方法二:

dfs:通过vector容器建立图的邻接表,从某个顶点开始搜索看是否能把其他所有顶点遍历(即是否全部联通),同时可用

vec.size()判断顶点的度数

#include<iostream>#include<cstring>#include<queue>#include<vector>#include<algorithm>#include<cstdlib>#include<cstdio>using namespace std;#define INF 0x3f3f3f3fconst int MaxV  = 1001;int vis[MaxV],N,M;  // N为顶点数,M为边数vector<int>vec[MaxV];void dfs(int i ){    vis[i] = 1; //将传进的源顶点(起点)标记1,表示已访问    vector<int>::iterator it;    for(it= vec[i].begin() ; it != vec[i].end() ;it++)//邻点接表深搜每个顶点的连接        if(vis[*it] == 0 )  //如果该点并未被访问过            dfs(*it);   //接着搜}int main(){    int n;    cin>>n;    while(n--){        memset(vis,0,sizeof(vis));        cin>>N>>M;        for(int i = 1 ; i <= N ; i++)            vec[i].clear();        for(int i = 0 ; i < M ; i++){ //邻接表            int u,v;            cin>>u>>v;            vec[u].push_back(v);            vec[v].push_back(u);        }        //判断是否为欧拉通路        dfs(1);        int cnt = 0 ;//cnt记录未被访问过的顶点,即判断图是否联通        int cnt2 = 0 ;//cnt2记录顶点度数为奇数的个数        for(int i = 1 ; i <= N; i++){            if(!vis[i])  //判断该顶点是否被访问过,若没说图没有全部联通                cnt ++;            if(vec[i].size()&1)                cnt2++;        }        if((cnt2 ==2 || cnt2== 0 ) && !cnt)                cout<<"Yes"<<endl;        else                cout<<"No"<<endl;    }return 0;}

方法三:

dbs:通过队列对图中某个顶点进行广度搜索。将与该点相连的其他任何点(边)都加入队列中,然后该点出列,并记录出列过点的总个数是否等于总顶点数(是否联通)。判断其他点与该点是否连接时可记录该点的度,便于决定是否是同路

#include<iostream>#include<cstring>#include<queue>#include<vector>#include<algorithm>#include<cstdlib>#include<cstdio>using namespace std;#define INF 0x3f3f3f3fconst int MaxV  = 1001;int vis[MaxV],N,M;  // N为顶点数,M为边数;int map[MaxV][MaxV];int odd,cnt;void bfs(){    int t,num;    queue<int>s;    s.push(1); /*从1开始*/    vis[1]=1; /*标记已访问该点*/    while(!s.empty()) /*队列非空*/    {        t=s.front(); //取队列首元素        s.pop();   //出列        cnt++;     //记录连接过的顶点个数        num=0;     //记录奇数点的个数        for(int i = 1 ; i <= N ; i++){            if(map[t][i]!=0) {/*判断出列的和其他点之间是否有连线*/                if(vis[i]==0) { /*i点还未访问*/                    s.push(i);                    vis[i]=1;                }                num++;//有连线说明度+1            }        }        if(num%2==1)//若度为奇数,记录加1            odd++;    }}int main(){    int n;    scanf("%d",&n);/*m组数据*/    while(n--)    {        odd=cnt=0;        memset(vis,0,sizeof(vis)); /*初始化都没有访问*/        memset(map,0,sizeof(map));/*初始化都没有连线*/        cin>>N>>M;        int u,v;        for(int i = 0 ; i < M ;  i++){            scanf("%d%d",&u,&v);            map[v][u]=1;            map[u][v]=1; /*建立图表*/        }        bfs();        if((N==cnt)&&((odd==0)||(odd==2)))  //图连通且有两个奇数点或没有奇数点            printf("Yes\n");        else            printf("No\n");    }    return 0;}



0 0
原创粉丝点击