历届试题 国王的烦恼 (并查集)

题目:

 C国由n个小岛组成,为了方便小岛之间联络,C国在小岛间建立了m座大桥,每座大桥连接两座小岛。两个小岛间可能存在多座桥连接。然而,由于海水冲刷,有一些大桥面临着不能使用的危险。

  如果两个小岛间的所有大桥都不能使用,则这两座小岛就不能直接到达了。然而,只要这两座小岛的居民能通过其他的桥或者其他的小岛互相到达,他们就会安然无事。但是,如果前一天两个小岛之间还有方法可以到达,后一天却不能到达了,居民们就会一起抗议。

  现在C国的国王已经知道了每座桥能使用的天数,超过这个天数就不能使用了。现在他想知道居民们会有多少天进行抗议。

输入格式

  输入的第一行包含两个整数n, m,分别表示小岛的个数和桥的数量。
  接下来m行,每行三个整数a, b, t,分别表示该座桥连接a号和b号两个小岛,能使用t天。小岛的编号从1开始递增。

输出格式

  输出一个整数,表示居民们会抗议的天数。

样例输入

4 4
1 2 2
1 3 2
2 3 1
3 4 3

样例输出

2

样例说明

  第一天后2和3之间的桥不能使用,不影响。
  第二天后1和2之间,以及1和3之间的桥不能使用,居民们会抗议。
  第三天后3和4之间的桥不能使用,居民们会抗议。

数据规模和约定

  对于30%的数据,1<=n<=20,1<=m<=100;
  对于50%的数据,1<=n<=500,1<=m<=10000;
  对于100%的数据,1<=n<=10000,1<=m<=100000,1<=a, b<=n, 1<=t<=100000。

思路:

这道题拿过来一看,嗯呐,并查集,再看看数据范围,直接上并查集肯定要炸,然后头铁的我去试了试,嗯呐,最后两组测试数据,运行超时,意料之中。

这道题,你需要注意居民抗议的条件(如果前一天两个小岛之间还有方法可以到达,后一天却不能到达了,居民们就会一起抗议。),所以你在用并查集的时候一定要注意判断前一天的状态和今天的状态的差距,然后判断居民会不会抗议。

可以用一个结构体存储桥的情况,按照时间由大到小进行排序,然后这样就可以逆向建树,在一次并查集中就可以成功的找出来答案。

代码如下:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;

int n,m;
int f[10010];

struct node
{
    int a;
    int b;
    int t;
} s[100010];

void init()//初始化;
{
    for(int i=0; i<=n; i++)
        f[i]=i;
    return ;
}

int getf(int v)//找boos节点;
{
    if(f[v]==v)
        return v;
    else
        return f[v]=getf(f[v]);
}

int merge(int u,int v)//判断连通性,合并;
{
    int t1=getf(u);
    int t2=getf(v);
    if(t1!=t2)
    {
        f[t1]=t2;
        return 1;
    }
    return 0;
}

bool cmp(node aa,node bb)//排序;
{
    return aa.t>bb.t;
}

int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=0; i<m; i++)
        {
            scanf("%d%d%d",&s[i].a,&s[i].b,&s[i].t);
        }
        sort(s,s+m,cmp);
        int sum=0,day=-1;//day记录的时间是上一次连通的时间;
        init();
        for(int i=0; i<m; i++)//时间由大到小;
        {
            if(merge(s[i].a,s[i].b)==1&&s[i].t!=day)//连通,但是时间和上一次联通的时间不同;
            {
                sum++;
                day=s[i].t;
            }
        }
        printf("%d\n",sum);
    }
    return 0;
}

错误代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;

int n,m;
struct node
{
    int a;
    int b;
    int t;
} s[100010];

int sum,maxx;
int f[10010];

void init()
{
    for(int i=0; i<=n; i++)
        f[i]=i;
    return ;
}

int getf(int v)
{
    if(f[v]==v)
        return v;
    else
        return f[v]=getf(f[v]);
}

void merge(int u,int v)
{
    int t1=getf(u);
    int t2=getf(v);
    if(t1!=t2)
        f[t1]=t2;
    return ;
}

int qq(int x)
{
    init();
    for(int i=0; i<m; i++)
    {
        if(s[i].t>=x)
        {
            merge(s[i].a,s[i].b);
        }
    }
    int s=0;
    for(int i=1; i<=n; i++)
    {
        if(f[i]==i)
            s++;
    }
    return s;
}

int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        maxx=-1;
        for(int i=0; i<m; i++)
        {
            scanf("%d%d%d",&s[i].a,&s[i].b,&s[i].t);
            maxx=max(maxx,s[i].t);
        }
        sum=0;
        int kk=qq(1);//初始的连通分量;
        for(int i=1; i<=maxx+1; i++)
        {
            int tt=qq(i);
            if(tt!=kk)//连通分量不同了,那么肯定有桥以前连通但是现在却不连通了,那么居民会抗议;
            {
                sum++;
                kk=tt;//连通分量改变;
            }
        }
        printf("%d\n",sum);
    }
    return 0;
}

 看看这简单粗暴的代码,不炸我都不好意思了。。。。。。。O(∩_∩)O

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值