【蓝桥杯】历届试题 国王的烦恼(并查集)(C++)

历届试题 国王的烦恼

  • 问题描述
  • 解题思路
  • 代码实现

问题描述

题目链接:国王的烦恼
资源限制
时间限制:1.0s 内存限制:256.0MB
问题描述
  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。

解题思路

思路代码参考:【蓝桥杯】历届试题 国王的烦恼(并查集)

读题时对题目中的如果前一天两个小岛之间还有方法可以到达,后一天却不能到达了,居民们就会一起抗议这句话没有正确理解。下面通过这个例子来理解一下这句话
【蓝桥杯】历届试题 国王的烦恼(并查集)(C++)_第1张图片
第一天:所有小岛均正常,没有桥损坏;
第二天:桥d损坏,由于小岛3和4之间第一天可以连通而第二天不能连通,因此小岛3和4居民抗议,抗议天数为1。如下图所示
【蓝桥杯】历届试题 国王的烦恼(并查集)(C++)_第2张图片
第三天:桥b损坏,小岛1和3可以通过小岛2进行连通,小岛1和3居民不进行抗议;此时有一个疑问:小岛3和4的居民不进行抗议吗?由于题中说如果前一天两个小岛之间还有方法可以到达,后一天却不能到达了,居民们就会一起抗议,小岛3和4第二天已经不连通,第三天依然不连通,因此小岛3和4的居民不会进行抗议。
【蓝桥杯】历届试题 国王的烦恼(并查集)(C++)_第3张图片
第四天:桥a损坏,小岛1和3由于第3天可以连通,但第4天不能连通,因此小岛1和小岛3居民进行抗议,抗议天数增加为2。如下图所示
【蓝桥杯】历届试题 国王的烦恼(并查集)(C++)_第4张图片
第五天:桥c损坏,小岛2和3由于第4天可以连通,但第5天不能连通,因此小岛2和3居民进行抗议,抗议天数增加为3。如下图所示
【蓝桥杯】历届试题 国王的烦恼(并查集)(C++)_第5张图片

思路:由于按照题目的描述去进行桥的损坏的模拟,由于桥的数量比较大,程序会运行超时。因此,逆向考虑问题,去模拟桥的修建。
1.只需要循环去联合(Union)这些桥,在进行Union操作时,一旦发现这个Union操作导致桥之间的连通发生了变化,则说明一定存在桥的损坏使两个小岛之间前一天连通,而后一天不连通,此时需执行ans++。
2.但是,当存在多个任意两个小岛之间的桥的使用天数相等时,抗议天数只会增加一天,但是上述操作会导致1中ans多次增加。为了避免相同天数导致ans多次增加,设置一个lastday变量,表示前一次某个桥的使用天数,如果当前桥的使用天数不等于lastday,则ans++,否则,ans不变。

代码实现

#include 
#include 
using namespace std;

const int N=10010;
const int M=100010;

struct Bridge//定义一个结构体表示桥 
{
	int x,y;//x,y分别表示桥相连的两个小岛 
	int day;//day表示桥的使用天数 
	Bridge(){};
	Bridge(int a,int b,int c):x(a),y(b),day(c){};
};

int pre[N];//用于存储每个小岛的"上一级"
Bridge bridge[M];//用于存储所有的桥 

void init(int n)//初始化每个小岛,让其上一级为其本身 
{
	for(int i=1;i<=n;i++)
	{
		pre[i]=i;
	}
}

int find_pre(int n)//找到某个小岛的祖先 
{
	if(pre[n]==n)
		return n;
	else return pre[n]=find_pre(pre[n]);

}

bool Union(int x,int y)//连通两个小岛,如果两个小岛不连通则返回真,连通返回假 
{
	int rootx=find_pre(x);
	int rooty=find_pre(y);
	if(rootx!=rooty)
	{
		pre[rootx]=rooty;
		return true;
	}
	else
		return false;
}

bool cmp(Bridge a,Bridge b)
{
	return a.day>b.day;
}

int main()
{
	int n,m,a,b,t;
	cin>>n>>m;
	init(n);//初始化每个小岛 
	for(int i=1;i<=m;i++)
	{
		cin>>a>>b>>t;
		bridge[i]=Bridge(a,b,t);
	}
	sort(bridge+1,bridge+1+m,cmp);
	int ans=0,lastday=0;//ans表示抗议次数,lastday表示一次某个桥的使用天数 
	for(int i=1;i<=m;i++)
	{
		bool flag=Union(bridge[i].x,bridge[i].y);//如果为真表示当前两个小岛为连通 
		if(flag&&bridge[i].day!=lastday)//未连通,且此桥的天数第一次出现,那么就增加了抗议天数 
		{
			ans++;
			lastday=bridge[i].day;
		}
	}
	cout<<ans<<endl;
	return 0;
} 

【蓝桥杯】历届试题 国王的烦恼(并查集)(C++)_第6张图片

你可能感兴趣的:(蓝桥杯)