【ybtoj】【最小生成树】【例题3】公路建设

公路建设


Link

传送门
题目


解题思路

这题的数据是蛮水的,好像暴力最小生成树就可以过去
但是,你看着一条边一条边加的,暴力写完你心里真的不难受吗 (强迫症)
每输入两个点之间的新边,将旧边替换成新边,冒泡排序(插排也星)(不是错别字qwq)
再做最小生成树,复杂度 O ( m 2 ) O(m^2) O(m2)

我们考虑加入这条边对先前求出的生成树的影响,发现要么这条边插入生成树,并踢出原先生成树中的一条边,要么树没变,还是之前那棵树,没在最小生成树上的边在之后不会成为最小生成树上的边,将这些边全部忽略掉。每次kraskal复杂度变为O(n),总复杂度仅O(nm) ————一本通

反正就是看不懂啦


Code

#include <iostream>
#include <cstdio>

using namespace std;

struct DT{
	int x, y, s;
}a[2100];
double ans;
int n, m, x, y, xx, yy, s, num, fa[510], p[510][510];

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

int main() {
	scanf("%d %d", &n, &m);
	for(int j = 1; j <= m; j++) {
		scanf("%d %d %d", &x, &y, &s);
		if (x > y)
			swap(x, y);

        //替换边
		if (!p[x][y]) {
			p[x][y] = ++num;
			a[num] = (DT){x, y, s};
		} else {
			if (s > a[p[x][y]].s) {
				if (ans == 0) printf("0\n");
					else printf("%0.1f\n", ans / 2.0);
				continue;
			}
			a[p[x][y]].s = s;
		}

        //冒泡排序
		if (a[p[x][y]].s < s) {
			for (int i = p[x][y]; i < num; i++)
				if (a[i].s > a[i + 1].s) {
					p[a[i].x][a[i].y] = i + 1;
					p[a[i + 1].x][a[i + 1].y] = i;
					swap(a[i], a[i + 1]);
				} else break;
		} else {
			for (int i = p[x][y]; i > 1; i--)
				if (a[i].s < a[i - 1].s) {
					p[a[i].x][a[i].y] = i - 1;
					p[a[i - 1].x][a[i - 1].y] = i;
					swap(a[i], a[i - 1]);
				}
		}
		
		//最小生成树
		for (int i = 1; i <= n; i++)
			fa[i] = i;
		ans = 0;
		for (int i = 1; i <= num; i++) {
			xx = find(a[i].x), yy = find(a[i].y);
			if (xx != yy) {
				fa[xx] = yy;
				ans += a[i].s;
			}
		}
		
		//判答案
		int flag = 0;
		for (int i = 1; i <= n; i++)
			if (fa[i] == i)
				flag++;
		if (flag > 1) ans = 0;
		if (ans == 0) printf("0\n");
			else printf("%0.1f\n", ans / 2.0);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值