【ybt高效进阶3-2-2】新的开始

52 篇文章 1 订阅

新的开始

题目链接:ybt高效进阶3-2-2

题目大意

有一个图,要你让所有点所在的连通块都至少有一个标记点。
两个点相连有一个规定的费用,把一个点标记也有费用。
要费用最小,输出最小费用。

思路

这道题看到判断连通,我们就想到并查集。然后最小,就想到最小生成树。

但是它不一定只选一个点标记,而且如果标记了不止一个点,整个图就可以不是整个连通的。
那怎么办呢?

那我们想想如果标记了不止一个点,那这些点之间会有怎样的关系。
可以想到,它们之间是可以当做任意连通的,那你可以把每个标记的点之间都弄上一条边,权值为 0 0 0

但你如果要想最小生成树那样搞的话,就不能像上面那样不停地加新的边。
那我们考虑这样弄:
我们把标记点也当做一条边,权值就是标记的费用,然后从自己连向自己。
那然后你就把所有边排序,像最小生成树一样搞。正常的边就正常地搞。对于标记的边我们分两种:
第一种,第一次遇到标记的边,那因为整个图中一定要标记至少一个,那我们是一定要标记这个点的。(因为你按费用从小到大排序,那你第一个找到哦的标记的一定就是所有中权值最小的)
第二种,不止第一次,那它其实就相当于跟前面标记过的点连边。那根据这样的方法,前面的标记过的点一定是连通了的,那我们只要随便选一个就可以了。当然,如果已经连通,就没有必要标记。

还有循环结束的条件就多了一个,不止是正常的边的数量要是 n − 1 n-1 n1,还要至少有一个点被标记。

代码

#include<cstdio>
#include<algorithm>

using namespace std;

struct node {
	int x, y, z;
}a[90001 + 100001];
int n, now, num, last, fa[301], line;
long long ans;

bool cmp(node x, node y) {
	return x.z < y.z;
}

int find(int now) {
	if (fa[now] == now) return now;
	return fa[now] = find(fa[now]);
}

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &a[i].z);
		a[i].x = a[i].y = i;//我们将标记点也看做一条边,连向自己
		
		fa[i] = i; 
	}
	
	now = n; 
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			scanf("%d", &a[now + 1].z);
			if (i > j) {
				now++;
				a[now].x = i;
				a[now].y = j;
			}
		}
	}
	
	sort(a + 1, a + now + 1, cmp);
	
	for (int i = 1; i <= now; i++) {
		if (a[i].x == a[i].y) {
			if (!last) {//至少要一个标记点
				last = a[i].x;
				ans += 1ll * a[i].z;
			}
			else {
				int X = find(a[i].x), Y = find(last);
				if (X == Y) continue;//如果跟之前标记的点在一个连通块里面就没必要标记
					else {
						fa[X] = Y;
						last = a[i].x;
						ans += 1ll * a[i].z;
					}
				line++;
				if (line == n - 1 && last) break;
			}
		}
		else {//正常的最小生成树
			int X = find(a[i].x), Y = find(a[i].y);
			if (X == Y) continue;
			fa[X] = Y;
			ans += 1ll * a[i].z;
			line++;
			if (line == n - 1 && last) break;
		}
	}
	
	printf("%lld", ans);
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值