nyoj 一笔画(并查集)

来源:互联网 发布:带网络驱动精灵 编辑:程序博客网 时间:2024/04/30 12:38

一笔画问题

时间限制: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

上篇用的是深搜,但是这一笔画不用并查集做就不叫做过这一笔画

而且建图的话 数组开的太大,数组的利用率也不高,如果数据较大,那不敢相信;

还是要用一下并查集了,而且这是学习并查集的好机会


简单的并查集

开一个数组,然后将数组初始化

初始化就是将数组里的值改为下标的值

如  a1 里面的值为1  a2  的值为2;

然后 当后面输入  路径(两点连接情况)时,

如果两个点相连,将数组里面的数变为同一个数

如  1 - 3  

那么把  a1   和a3  里面的数变为 1  (3也可以,不过后面的都要这样变)

再来一组  1- 4   a1  a4都要变成1(或者3)

数组里面数相同的表示同一个数

  如果来一组    2 - 6 那么 a2  与  a6 都为 2  (或者为6)

这样  目前有两组了;

下面先具体实现用此方法的 判断连通图


#include<stdio.h>int point,line;int a[1005],m[1005],n[1005];int bh(int a,int b){    if(a>=b)    {        return b;    }    else    {        return a;    }}int main(){    int t;    scanf("%d",&t);    while(t--)    {        int flag=1;        scanf("%d%d",&point,&line);        for(int i=1; i<=point; i++) //这里的i=1 i<=point 不可以换成i=0 i<point        {            a[i]=i;        }        for(int i=0; i<line; i++) //输入的同时开始合并一些集合        {            scanf("%d%d",&m[i],&n[i]);            a[m[i]]=bh(a[m[i]],a[n[i]]);            a[n[i]]=bh(a[m[i]],a[n[i]]);        }        for(int i=1; i<point; i++) //同上 不可以换        {            if(a[i]!=a[i+1])            {                flag=0;                break;            }        }        if(flag==0)        {            printf("NO\n");        }        else        {            printf("YES\n");        }    }}

这样一个判断是否为连通图的代码就来了

如果是  输出YES  否则NO

但是,刚开始没发现,后来dbug的时候发现了

这样判断连通图是有错误的

6 6

2 3

3 4

1 2

6 5

4 5

1 6

这组数据是不对的,因为我每一次合并两个集合时只是把这个集合的一个数拉过来

其余的并没有考虑  下面修改代码

下面直接贴上AC代码

#include<stdio.h>#include<string.h>int a[1005],d[1005],m[2005],n[2005];int min(int a,int b){    if(a>=b)    {        return b;    }    else    {        return a;    }}int max(int a,int b){    if(a<=b)    {        return b;    }    else    {        return a;    }}int main(){    int t;    scanf("%d",&t);    while(t--)    {        int point,line,x1,x2;        int flag=1,num=0;        scanf("%d%d",&point,&line);        memset(d,0,sizeof(d));        memset(a,0,sizeof(a));        for(int i=1; i<=point; i++)   //初始化        {            a[i]=i;        }        for(int i=1; i<=line; i++) //输入的同时开始合并一些集合        {            scanf("%d%d",&m[i],&n[i]);            x1=max(a[m[i]],a[n[i]]);            x2=min(a[m[i]],a[n[i]]);            //printf("%d %d\n",x1,x2);            for(int j=1;j<=point ;j++)            {                if(a[j]==x1)                {                    a[j]=x2;                }            }            d[m[i]]++;            d[n[i]]++;            /*for(int j=1;j<=point ;j++)            {                printf("%d ",a[j]);            }            printf("\n");*/        }        for(int i=1; i<point; i++) //判断是否为连通图        {            if(a[i]!=a[i+1])            {                flag=0;                break;            }        }        for(int i=1; i<=point; i++)        {            if(d[i]%2==1)            {                num++;            }        }        //printf("%d\n",num);        if(flag==1&&point>1)        {            if(num==2||num==0)            {                printf("Yes\n");            }            else            {                printf("No\n");            }        }        else if(point==1)        {            printf("Yes\n");        }        else        {            printf("No\n");        }    }    return 0;}

错误之处就是修改时  要从头遍历一遍,如果符合修改条件  那就一块修改了,不能只修改一个

  

啊啊啊,这并查集我怎么觉得比递归还时间长

每输入一个就要从头便利一遍,是不是我方法错误的

下一篇文章我再来一遍这道题,并查集优化法


这篇比较惨





1 0
原创粉丝点击