Sweety

Practice makes perfect

导航

Play on Words(半欧拉图)

Posted on 2014-11-14 22:22  蓝空  阅读(519)  评论(0编辑  收藏  举报

打开题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1116

 

在做这道题的之前,有必要先对几个概念进行了解

 

注意网络上的有向图的对连通图的定义 相当于 曲婉玲版的离散数学中讲的强连通图,无向图就一样了

(一)通路与回路:

            给定图,设G中顶点和边的交替序列为,若满足如下条件:的端点(在G是有向图时,要求的始点,

      的终点),,则称为顶点通路分别称为此通路的起点和终点,中边的数目l称为长度。当=时,此通路称为

     回路。 =========>>>>>  回路也是通路

(二)欧拉通路与欧拉回路:

           欧拉通路: 通过图中每条边且只通过一次,并且经过每一顶点的通路。

          欧拉回路: 通过图中每条边且只通过一次,并且经过每一顶点的回路。

           =========>>>>>  欧拉回路也是欧拉通路

         判定

         无向图是否具有欧拉通路或回路的判定:

         欧拉通路:图连通;图中只有0个或2个度为奇数的节点欧拉回路也是欧拉通路)

        欧拉回路:图连通;图中所有节点度均为偶数

 
        有向图是否具有欧拉通路或回路的判定:

        欧拉通路:图连通;除2个端点外其余节点入度=出度;1个端点入度比出度大1;一个端点入度比出度小1 或 所有节点入度等于出度

        欧拉回路:图连通;所有节点入度等于出度

 

 (三)欧拉图与半欧拉图:

           欧拉图:具有欧拉回路的图称为欧拉图

           半欧拉图:具有欧拉通路但是没有欧拉回路的称为半欧拉图

                         =========>>>>>  欧拉图不是半欧拉图

 

    判定

            无向图:
            欧拉图:连通图+没有奇度顶点
           半欧拉图:连通图+两个奇度顶点


           有向图:
           欧拉图:连通图+入度等于出度
           半欧拉图:连通图+ 两个奇数顶点(出度一个入度一个)+其他度

          注意屈婉玲版教材中定义的冗余,只要是连通图就可以了

 

本题大意:

 输入n,然后输入n个单词,判断是否存在每一个单词词首能和另一个单词的词尾相同,这样连起来能构成为一条链

解题思想:

其实就是判断一个欧拉回路,其中会用到并查集和路径压缩,也就是现将其转化为树,通过树根的多少来判断是否连通,如果连通,再加上欧拉图的充要条件也就是是否只有两个奇度顶点且一个的入度-出度==1,另一个出度-入度==1

其实这种方法只能是作为一个欧拉回路的判断方法,路径是记录不下来的,这只能是在路径查找中辅助性的判断,但是思想挺重要的

 

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;


int in[26],out[26];//入度,出度
int differ[10];//入度不同的顶点
bool exist[26];//记录字母是否存在
int set[26];//父亲节点
int root;//根的数目

int set_find(int d){  
    if(set[d]<0) 
        return d;  
    return set[d]=set_find( set[d] );  
}  


void join(int y,int x){  //采用路径压缩的并查集,判断是否连通
   x=set_find(x); 
   y=set_find(y);  
    if(y!=x) 
		set[y]=x;  
    return;
} 


int main(){
    int t;//测试个数
    int n;//单词个数
    int i;
    int number;//入度不同的顶点的个数
    char word[1100];//存放单词
    int x,y;
    scanf("%d",&t);
    while(t--){
        memset(in,0,sizeof(in));//入度
        memset(out,0,sizeof(out));//出度
        memset(set,-1,sizeof(set));//父亲节点
        memset(exist,0,sizeof(exist));//记录字母是否存在
        number=0;
        root=0;
        scanf("%d",&n);
        while(n--){//输入,并且将字符串变成数
            scanf("%s",word);
            x=word[0]-'a';
            y=word[strlen(word)-1]-'a';
            exist[x]=exist[y]=true;
            out[x]++;
            in[y]++;
            join(y,x);
        }
        for(i=0;i<26;i++){
            if( exist[i] && set[i]<0 ) 
				root++;
		}
		if(root>1)   //图不连通原因:根节点个数不同肯定不连通
			printf("The door cannot be opened.\n");
		else
		{
			for(i=0;i<26;i++)
			{
				if(in[i]!=out[i])
					differ[ number++ ]=i;
			}
			if(number==0) //所有顶点入度等于出度,为欧拉图
				printf("Ordering is possible.\n");


			else 
				//两个奇数顶点(一个出度、一个入度)
				if( number==2 && ( (in[differ[0]]-out[differ[0]]==1 && out[differ[1]]-in[differ[1]]==1)||//注意&&、||的优先级,此处要加括号
				     (out[differ[0]]-in[differ[0]]==1 && in[differ[1]]-out[differ[1]]==1)  ) )//满足半欧拉图条件,为半欧拉图
			     	printf("Ordering is possible.\n");

			    else printf("The door cannot be opened.\n");//不是欧拉图,也不是半欧拉图
		}
    }
    return 0;
}