POJ-1308 & HDOJ-1325 Is It A Tree? 解题报告

       这是一道并查集类型的题目,较容易。题意是说给定每个互相连接的节点的编号,判断这些节点是否能够形成树形结构并按题目要求根据情况进行输出。满足树形结构的要求是

1)只有一个根节点,也就是说这个根节点没有父节点(或者说父节点指向自己);

2)根节点以外的每个节点有且只有一个父节点(当然每个节点可有多个子节点);

3)从根节点到达其他任意一个节点有且只有一条路线(解释的更简单些就是这些节点不能够产生环);


       注意:这道题有些特殊情况还是有点令人想不到的,比如直接输入0 0的话,是一个空树,按照满足树形结构的要求来输出,还有就是如果输入的数据有多个树形结构(也就是森林),按照不满足树形结构的要求来输出。


       接下来讲解题思路,用并查集实现这道题,当输入的两个节点不属于同一个结构时(也就是没有共同的根节点),将这两个节点进行合并操作,而如果输入的两个节点属于同一结构时(有同一个根节点),说明这个结构产生了环,那么这个结构肯定不属于树形结构。

       输入0 0时代表这个结构输入结束,如果只输入了0 0可以做特殊判断解决。现在讲讲如果出现了森林应该如何解决。首先要知道一个树形结构的节点数n与边数s的关系显然是n = s + 1,因为不出现环结构。我们在输入时记录节点数以及边数,最后输出的时候判断节点数与边数是否满足这个公式就ok了。


       下面是我的解题代码

#include <stdio.h>
#include <stdlib.h>
#define N 50001

int bleg[N];        /*存储父节点*/
int number = 1;     /*case数*/
int ferr = 0;       /*标志变量,1代表非树形结构*/
int num[2*N];       /*存储节点的编号*/
int n = 0;          /*表示num数组存储节点的数量*/
int side = 0;       /*边的数量*/
char *it = "is a tree.";
char *nt = "is not a tree.";


void Init();    /*初始化*/

int Find(int x);    /*查找操作*/

void Union(int x, int y);   /*合并操作*/

void Judge();   /*判断节点数与边数是否满足树形结构的公式*/

int Mycompare(const void *a, const void *b);    /*qsort函数的比较函数*/

int main()
{
    int x, y;
    Init();
    while (~scanf("%d %d", &x, &y))
    {
        if (x == y && x == 0)
        {
            Judge();        /*判断*/
            if (ferr == 1)
            {
                printf("Case %d %s\n", number++, nt);
            }
            else
            {
                printf("Case %d %s\n", number++, it);
            }
            Init();         /*初始化*/
        }
        else if (x == y && x == -1)
        {
            break;
        }
        else
        {
            num[n++] = x;   /*将两个节点存入数组num*/
            num[n++] = y;
            if (Find(x) == Find(y)) /*构成环结构*/
            {
                ferr = 1;
                continue;
            }
            else
            {
                side++;
                Union(x, y);
            }
        }
    }
    return 0;
}

void Init()
{
    int i;
    for (i=1; i<N; i++)
    {
        bleg[i] = i;
    }
    for (i=0; i<2*N; i++)
    {
        num[i] = 0;
    }
    ferr = 0;   /*初始化标记变量*/
    side = 0;   /*初始化树形结构的边数*/
    n = 0;      /*初始化num数组存储的节点数*/
    return;
}

int Find(int x)     /*查找操作*/
{
    int y = bleg[x];
    int z;
    while (y != bleg[y])
    {
        y = bleg[y];
    }
    while (x != bleg[x])    /*路径压缩*/
    {
        z = bleg[x];
        bleg[x] = y;
        x = z;
    }
    return y;
}

void Union(int x, int y)    /*合并操作*/
{
    int fx = Find(x);
    int fy = Find(y);
    bleg[fx] = fy;
    return;
}

void Judge()        /*判断节点数与边数是否满足树形结构的公式*/
{
    int i;
    int x = num[0];
    int kind = 1;       /*种类数,因为存储的节点编号会有重复*/
    qsort(num, n, sizeof(int), Mycompare);      /*先给这些节点编号排序*/
    for (i=1; i<n; i++)
    {
        if (num[i] != x)
        {
            x = num[i];
            kind++;
        }
    }
    if (kind != side + 1)   /*不满足树形结构的边与点公式*/
    {
        ferr = 1;
    }
    return;
}

int Mycompare(const void *a, const void *b)     /*qsort函数的比较函数*/
{
    return (*(int *)a - *(int *)b);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值