蓝桥杯 试题 历届试题 国王的烦恼 并查集
1、解这样的题,也可以有其他的方法,最好的方法就是采用并查集来解。
关于并查集的基本算法的讲解和代码,我们PPT中有。
2、分析题意画示意图:
(1)输入:
(2)小岛与桥示意图:
但是并查集没有办法表示集合的增加,只能表示集合的减少。
所以我们逆向思维,
从第四天到第三天,集合减少,人民满意度增加(可以过桥)。反过来就等于,从第三天到第四天,集合增加,人民抱怨。
从第三天到第二天,集合减少,人民满意度增加(可以过桥)。反过来就等于,从第2天到第3天,集合增加,人民抱怨。
从第二天到第一天,集合数保持不变。所以满意度和抱怨度都不变。
在这样的分析基础上,再结合并查集的代码,一起来解决这个集合类的问题。
3. 算法实现如下:
package qs;
/*
*
* 测试数据为:
4 4
1 2 2
1 3 2
2 3 1
3 4 3
*
*
*
*/
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Scanner;
public class testunionfind {
static int s[];
// static Bridge[] b=new Bridge[1000000+1]; 这个数组排序无力,去掉,数据结构选择有问题
static ArrayList<Bridge> blist=new ArrayList<Bridge>();
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();//小岛的个数
int m=sc.nextInt(); //桥的数目
s=new int[n+1];//下标0没有使用
for(int i=1;i<=m;i++) //读取m个桥的信息
{
Bridge bdg=new Bridge();
bdg.a=sc.nextInt();
bdg.b=sc.nextInt();
bdg.t=sc.nextInt();
blist.add(bdg);
}
System.out.println(blist);//这一条是测试语句
sort(blist); //按天数排序 blist传给参数blist2后,不知道能不能实现排序的目的??
//传参行不行???????
// Collections.sort(blist,new Comparator<Bridge>(){
// //override
// public int compare(Bridge o1,Bridge o2){
// return o2.t-o1.t;
// }
// });
System.out.println(blist);
init(n); //先把小岛孤立的初始化为分散的并查集
int a=-1; //a用来判断前后抗议的天数是不是在同一天
int sum=0; //sum用来表示抗议的天数
for(int i=0;i<m;i++) //倒叙得到m个桥,jingxing合并。合并时并查集-1,则抗议+1
{ //得到第i个桥梁,它可以把两个小岛合并
int b= mergee(blist.get(i).a,blist.get(i).b); //把桥两端的岛屿合并
if(b==1)//如果不属于一个集合而合并成功为一个集合
{
if(blist.get(i).t!=a)//与上一个抗议的日期不在同一天
{sum++;
a=blist.get(i).t;
}
}
}
System.out.println("sum="+sum);
}
//把桥按照天数逆序排序,逆向思维进行建立桥梁 每当建立一个桥梁,集合减少,说明没有这所桥梁时,
//有两个孤立的集合存在,那么老百姓无法过桥到另外一座岛屿上,则老百姓在抱怨
// 传参可
private static void sort(ArrayList<Bridge> blist2) {
Collections.sort(blist2,new Comparator<Bridge>(){
//override
public int compare(Bridge o1,Bridge o2){
return o2.t-o1.t;
}
});
}
//
private static int mergee(int x,int y) { //并查集把有关系的和并在一起
// TODO Auto-generated method stub
x=findx(x);
System.out.println(x);
y=findx(y);
System.out.println(y);
if(x!=y)
{s[x]=y;
return 1;//return 1说明本来是不连通的(不在一个集合中的)
}
else
return 0;//return 0说明本来是在一个集合中的
}
private static int findx(int x) //并查集查询并优化
{ if(x!=s[x])
s[x]=findx(s[x]); //查询并优化
return s[x];
}
public static void init(int n) //并查集初始化
{
for(int i=1;i<=n;i++)
{s[i]=i; //s[]是并查集的基本数据结构
System.out.println("s[i]"+s[i]);
}
}
}
class Bridge{
//Bridge是个对象 a,b, 是桥连接的小岛的号。t是 天数
public int a,b,t;
public Bridge(){
}
public Bridge(int a,int b,int t)
{this.a=a;
this.b=b;
this.t=t;}
public String toString()
{return a+" "+b+" "+t;}
}
4.
总结:
首先,该程序中用到了 并查集的概念。并妥善使用了初始化,查询,合并的功能。
其次,该程序中用到了ArrayList来表示有序集合元素,比数组要好用一些。
再次。用到了Collections的sort功能。它可以对一个列表进行排序,且列表中的元素是对象,然后按照对象中的某一项的大小进行比较,采用降序的形式,排序效果好。