兽径管理

兽径管理

题目传送

前言

第一次看这题的时候,被输出吓到了,有长有短(其实那些是注释...)

真正做这题是同桌喊我做的,其实这题很简单


算法

  • 插入排序 & \(Kruskal\)求最小生成树

  • 倒序\(Kruskal\)求最小生成树

会用两种方法讲解


插入排序

本种做法思路很简单:

  1. 不用转换问题,直接每次更新出一条边就将该条边存入可使用的边集

  2. 然后跑最小生成树,如果根本没有最小生成树,则是"-1",反之输出边权和

但是显然,时间复杂度会很高,因为每更新一条边就要对整个边集重新进行排序

那怎么优化一下?

用插入排序代替\(sort\)

因为本人太懒不想介绍插入排序,就直接引用一下别人的博客吧qwq

(建议使用\(vector\)实现插入排序),跑出来900+ms


倒序\(Kruskal\)

重点介绍这种做法

不得不说,倒序的思想在做题中十分重要!

因为很多按序开仓(或按序刷新)的题通过倒序思想一转换就很简单了

这题也是一样的:

我们将正序依次开放改为倒序关闭

这样做的好处就是我们只需要排一次序,而不是每加入一条就刷新一次

感觉直到了倒序之后这题就好做了,直接给出代码,900+ms:

#include <bits/stdc++.h>
using namespace std;
int n,m,a,b,c,now,ans,fa[520010],sum[520010],flag[520010];

struct node {
	int u,v,w,id;
} e[520010];

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

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

inline void kurskal() {
	for(register int i=1;i<=n;i++) fa[i]=i;
	now=0;ans=0;
	for(register int i=1;i<=m;i++) {
		if(flag[i]==1) continue;
		int x=find_fa(e[i].u);
		int y=find_fa(e[i].v);
		if(x!=y) {
			fa[x]=y;
			now++;
			ans+=e[i].w;
		}
		if(now==n-1) break;
	}
}

int main() {
	scanf("%d%d",&n,&m);
	for(register int i=1;i<=m;i++) {
		e[i].id=i;
		scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
	}
	sort(e+1,e+1+m,cmp);
	for(register int i=m;i>=1;i--) {
		kurskal();
		if(now!=n-1) {
			sum[i]=-1;
			for(register int j=1;j<i;j++) sum[j]=-1;
			break;
		}
		else sum[i]=ans;
		for(register int j=1;j<=m;j++) {
			if(e[j].id==i) flag[j]=1;
		}
	}
	for(register int i=1;i<=m;i++) printf("%d\n",sum[i]);
	return 0;
} 

最后,如果有任何问题,欢迎留言区评论,我会及时回复、改正,谢谢orz


posted @ 2020-07-24 09:34  Eleven谦  阅读(19)  评论(0编辑  收藏