这个是本周AcWing的周赛题,但是我完全被第二题就绊住了,也没怎么看第三题。虽然看了我也写不出来。
看了一下y总的讲解,现在记录一下这道题的解法。
刚才又看了一下讲解,原来第i个要求中添加i条边不是在i-1个需求的基础上再添加i-1条边,而且相对于最开始的空图添加i条边,并且满足前i个需求。要注意这一点!
然后我们来分析题目:题目说的是让两个点连通,联通的话可以认为这两个集合属于同一个集合。那么我们可以用并查集维护若干个集合,如果两个点是联通的,那么这两个点就会属于同一个集合。
好,如果目前需要联通的两个点属于两个集合,那么需要几条边来完成对这两个点的联通呢?因为一个集合内部的所有点都是互相连通的,所以如果让两个点联通,相当于让这两个集合联通,那么只需要一条边就可以实现两个集合的联通。这就相当于两个村庄之间的建交,只要保证2个村庄中各出一个人,这两个人熟悉起来了,其他人也就全部能联系上了。
所以,这里有一个规律:让任意两个点联通,最多需要1条边。
那么再考虑一件事:如果我们现在有x条边,我们最多能让多少个集合联通呢?答案是x+1个集合。
题目中还有1个要求,需要保证所有点的度数的最大值最大,也就是说在所有的集合中找到一个点,这个点和最多的点有边相连。那么我们如何保证在指定条边的情况下,某个点的度数能够达到最大呢?很明显,让这个点成为中心,让每一条边都是由他辐射出去的。
很明显,两个集合都是完全联通的集合,但是第二个图中的最大度数为5.所以我们为了保证度数的最大值是最大的 ,那么就要保证这个集合内部的连接情况是菊花图式的。那么这个集合中的最大度数是多少呢?目前有x个点,那么最大度数就是x-1;
因为题目也不要求输出具体的边,所以我们只要考虑这个最大度数是多少,然后输出就好了。
好了,预备知识全部整理完毕,现在就针对一个具体的请求i进行分析吧。
首先对于第i个请求中的x和y,我们可能存在两种情况:
1.x和y本就是一个集合内部的,那么他们本来就是联通的,不需要添加边了,剩下一条边。这条边我们可以用来干嘛呢,当然是去和其他的集合进行联通了。
2.x和y分属于两个集合,那么就得在这两个集合中添加一条边,使得这两个集合进行合并了。那么这次的联通没能攒下这条边。
我们在每轮进行联通的时候都会统计一下当前多余的边的情况,然后看看这些多余的边还能联通几个其他的未联通的集合。
所以是不是很清晰的思路了?首先让这两个点x和y进行联通,然后统计目前多余的边,用这些边去联通其他的集合,而且联通的这些集合最好是集合内的元素越多越好,这样才能保证我们的最大度数越大。当所有多余的边都用光了,或者已经没有没联通的集合了,那么我们就看看这个集合中一共有多少个元素。最大的度数肯定是这个最大集合中的一个元素作为中心,从它向集合中的所有其他元素进行连线,所以最大度就是n-1.
这个题每一问都是独立的,所以暗示我们什么呢?就是上一次的连法和这一次的可能不同,但是我们可以借鉴上一次的。所以我们才可以把所有的点归到一个集合中,再分配边,以此来构造最大的度。
那么代码出来了:
int find(int a){
if(p[a] != a) p[a] = find(p[a]);
return p[a];
}
int main(){
int n, d;
scanf("%d%d", &n, &d);
for(int i = 1; i <= n; i++){
p[i] = i;
s[i] = 1; //不同集合里面的元素数量
}
int cnt = 0; //多余的边
for(int i = 0; i < d; i++){
int a, b;
scanf("%d%d", &a, &b);
a = find(a);
b = find(b);
if(a != b){
p[a] = b;
s[b] += s[a]; //合并这两个集合
}else{
cnt++; //这两个点已经联通了,所以多余了一条边
}
int tt = 0;
for(int i = 1; i <= n; i++){
if(find(i) == i) sz[tt++] = s[i]; //每个集合中元素的个数
}
sort(sz, sz + tt, greater<int>()); //把元素个数从大到小排序
int sum = 0;
//cnt条边可以连接cnt+1个集合
for(int i = 0; i < tt && i < cnt + 1; i++){ //将元素个数最大的几个集合进行连接
sum += sz[i];
}
cout<<sum - 1<<endl; // 这么多元素,所以度数等于元素数-1
}
return 0;
}
参考:主播,我们是不是你带过的最差的一届学生?AcWing杯 - 第92场周赛_哔哩哔哩_bilibili
就写到这了,昨天被y总和y嫂的爱情狠狠嗑到,其实都没好好听分析。
马上去打leetcode周赛,这次争取做完两题/(ㄒoㄒ)/~~