LeetCode:国王的烦恼之居民抗议的天数

问题描述
  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

解题思路:
这道题的关键地方要理清小岛居民抗议的转折点,根据题目抗议与否是由两个小道岛在某两天之间是否发生了连
通分支增加的事件所决定的,也就是说,一旦某个桥发生了损坏导致了两个小岛不互通,那么这小岛的居民就会在
这一天发生议,以后的天数就不会发生抗议.由于某桥发生损坏不能互通,居民可能通过其他桥进行互通,按照顺
序遍历不好推断.这道题可以逆序,不去判断桥什么时候坏掉,而是模拟何时开始建桥,这样一来字需要循环一次
去联合(unite)这些桥就ok,一旦出现某座桥在进行unite操作时,也就是unite操作导致了其连接的两个小岛发
生了元变化,换言之表示这个桥的损坏会使某两个小岛的居民由前一天的互通变成后一天的不互通,这样居民就
会产生抗议,即ans++.

根据题目例子,首先对bridge数组进行降序排序,
(1)第4天,是4座互相孤立的小岛,此时所有居民均不互通.
(2)第3天,小岛3和小岛4的桥被修建好,也就是第三天时小岛3和4的居民是互通的,第4天就不互通了,则居民就
会发生抗议,ans+1.
(3)第2天,修建了小岛1和小岛2,此时小岛1和2的居民是由第3天的不互通变成互通.正向就是di2天时小岛1和
2的居民是互通,但是第3天就不互通了,则居民会抗议,ans+1.
此外,在第2天时,还有小岛1与小岛3发生互通,按道理也会发生抗议,但它们都处于同一天,所以只能增加一次.因
此可以设置一个变量lastDay来进行规避多加的问题,lastDay记录前一次某桥的使用天数,在枚举过程中,若当
前桥的使用天数不等于lastDay,则对ans+1操作,否则表明同处于一天,不需要+1操作.
(4)第1天,小岛2和小岛3发生互通,但由于这一天前小岛3能通过小岛1到达小岛2,因此不需要执行ans+1.

C++语言实现

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

struct Bridge{
    int x,y;
    int day;
    Bridge(){}
    Bridge(int _x,int _y,int _day):x(_x),y(_y),day(_day){}
};

class Solution{
public:
    Solution():bridge(_n),pre(_m){}
    int protestofDays(){
        int n,m; //n小岛的个数和m桥的数量 
        init(n,m);
        sort(bridge.begin()+1,bridge.begin()+1+m,[](const Bridge& a,const Bridge& b){
            return a.day>b.day;
        });

        int ans=0,lastDay=0;
        for(int i=1;i<=m;i++){
            bool flag=unite(bridge[i].x,bridge[i].y);//传入小岛编号
            if(flag&&bridge[i].day!=lastDay){//满足不是前一天,而且刚建桥
                ans++;
                lastDay=bridge[i].day;//更新天数
            }
        }
        cout<<endl;
        return ans;
    }

private:
    void init(const int& n,const int& m){
        scanf("%d %d",&n,&m);
        for(int i=1;i<=n;i++)
           pre[i]=i;//代表小岛
        int a,b,t;//连通a号和b号两个小岛,能使用t天
        for(int i=1;i<=m;i++){
            cin>>a>>b>>t;
            bridge[i].x=a,bridge[i].y=b,bridge[i].day=t;
        }
    }

    bool unite(int _x,int _y){
        int x=findPre(_x),y=findPre(_y);
        if(x!=y){//判断两个小岛之前的情况
            pre[x]=y;
            return true;//互通
        }
        return false;
    }
    int findPre(int n){//传入小岛编号
        if(pre[n]==n) return n;
        return pre[n]=findPre(pre[n]);
    }
private:
    vector<int> pre; //每个小岛的"上级",代表小岛的编号
    static constexpr int _m=10010;//小岛的最大数量10000
    static constexpr int _n=100010;//桥的最大数量100000
    vector<Bridge> bridge; //若没有static时,vector数组的初始化根据声明private成员的顺序有关
                           //,若写在n的前面,n默认为0,则bridge.size为0,写在后面满足条件
};

int main(int argc,char* argv[]){
    cout<<Solution().protestofDays()<<endl;
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

路上的追梦人

您的鼓励就是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值