c++中关于最小生成树的例题一枚

 题目:

问题 C: 戴森球计划

时间限制: 1.000 Sec  内存限制: 256 MB
提交 状态

题目描述

你在游戏《戴森球计划》中来到了一个新的星球,并放下了n个生产设备。虽然星球是个球形,但是可以将其抽象成一个二维平面,其中第i个生产设备的坐标为xi,yi。
你想让每个生产设备都通上电。第i个设备能通上电,当且仅当能满足以下一个条件:
- 为第i个设备建造一个火力发电厂(或者微型聚变发电站或者人造恒星),这将花费ci元;
- 选择另一个有电的设备j(j≠i),在设备i和设备j之间拉起电线(其实在游戏里是电力感应塔无线输电塔或者卫星配电站)。

设备i和设备j之间安装电线的价格是每公里ki+kj元,其中ki,kj分别是设备i和设备j的用电量。电线只能走基本方向(北、南、东、西)。电线可以相互交叉但互不影响。综上,如果设备i和设备j通过电线连接,则电线的长度为|xi−xj|+|yi−yj|公里。因此,在设备i和设备j之间拉电线的花费是(ki+kj)(|xi−xj|+|yi−yj|)。
你想用尽可能少的钱来让每个电线都能通上电。你需要输出这一最小花费。
注意:即使当i≠j时,(xi,yi)=(xj,yj)也是可能的。

输入

第一行有一个整数id(1≤id ≤20),表示测试点编号。你可以用这个信息来判断每个测试点的特殊性质(具体见数据范围与约定)。
第二行有一个整数n,意义如题。
然后有n行,第i+2行有两个整数xi和yi,表示第i个设备的坐标。
紧接着一行n个整数,分别是c1,c2,...,cn。
最后一行n个整数,分别是k1,k2,...,kn。

输出

输出一个整数,表示让所有设备通上电的最小花费。

样例输入 Copy
【样例1】
1
5
1 2
1 93
1 9
1 38
1 8
485167613 784430249 637393481 271981451 436803228
1 1 1 1 1
【样例2】
20
3
2 1
1 2
3 3
23 2 23
3 2 3
样例输出 Copy
【样例1】
271981633
【样例2】
27
提示

样例解释

在样例1中,在第4个设备建设发电站,其他4个设备通过电线连向第4个设备。
在样例2中,在第2个设备建设发电站,另外两个设备通过电线连向第2个设备,总花费是2+10+15=27。
 

解体思路:

1.首先要看出这是一个 (最小)生成树 森林 问题。一开始其实先是想用dfs,然后想看看dp,但都不行。以后这种题目,要学会把他抽象成数!->看到题目中有“最小花费”,又给了每个点的获得方式。一开始我不知道如何抽象成树,但仔细思考题目确实像是给了一个“森林”;例如,两个发电方式:自己发电(即为和原点连接的距离),与其他数共享发电(即与其他树连接的距离)。所以,最优解即为构成森林的边权和的最小值;

那么在此来补一下最小生成树的概念:

*给无向图,若他的某个子图中任意两个顶点都相互连通,且是一棵树,则这棵树就叫该图的最小生成树。

*若边上有权值,那么边权和最小的生成树,就叫做最小生成树(MST)。

注意:只有连通图才有最小生成树,而对于非连通图,只存在生成树森林。

例如:(懒得打字,贴图了...)

(好像跑题了,有关最小生成树还会花笔墨再拿一篇讲的...)

2.其次,用排序算法,可以得出最优解;

3.并查集在数里也用的很多,下面是摘录的网上的:

并查集(Union Find),又称不相交集合(Disjiont Set),它应用于N个元素的集合求并与查 询问题,在该应用场景中,我们通常是在开始时让每个元素构成一个单元素的集合, 然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在 哪个集合中。虽然该问题并不复杂,但面对极大的数据量时,普通的数据结构往往无 法解决,并查集就是解决该种问题最为优秀的算法。

使用森林存储集合之间的关系,属于同一集合的不同元素,都有一个相同的根节点 ,代表着这个集合。

当进行查找某元素属于哪个集合时,即遍历该元素到根节点,返回根节点所代表的集 合;在遍历过程中使用路径压缩的优化算法,使整体树的形状更加扁平,从而优化查 询的时间复杂度。 当进行合并时,即将两颗子树合为一颗树,将一颗子树的根节点指向另一颗子树的根 节点;在合并时可按子树的大小,将规模较小的子树合并到规模较大的子树上,从而 使树规模更加平衡,从而优化未来查询的时间复杂度。

                                    原文链接:https://blog.csdn.net/lucifer_24/article/details/102295059

代码如下:(借鉴版)

#include<iostream>
#include<algorithm>
using namespace std;
int n,ans,id;
struct NODE{
	int x,y,c;
}node[100010];

struct EDGE{
	int u,v,l;
}edge[1000100];

int c[1000010],k[1000010],w;
int fa[100010];
//并查集的算法 
int find(int i){
	if(fa[i]==i){
		return fa[i];
	}
	fa[i]=find(fa[i]);
	return fa[i];
}
void unionn(int i,int j){
	int fa_i=find(i);
	int fa_j=find(j);
	fa[fa_i]=fa_j;
}
//排序 (从小到大) 
bool check(struct EDGE i,struct EDGE j){
	return i.l<j.l;
}
int main()
{
	cin>>id;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>node[i].x>>node[i].y;
	}
	for(int i=1;i<=n;i++){
		cin>>c[i];
		edge[w++]=EDGE{0,i,c[i]};
	}
	for(int i=1;i<=n;i++){
		cin>>k[i];
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<i;j++){
			int u=1ll*(k[i]+k[j])*(abs(node[i].x-node[j].x)+abs(node[i].y-node[j].y));
			edge[w++]=EDGE{i,j,u};
		}
	}
	sort(edge,edge+w,check);
	for(int i=0;i<w;i++){
		cout<<edge[i].l<<endl;
	}
	for(int i=1;i<=n;i++){
		fa[i]=i;
	}
	for(int i=0;i<w;i++){
		int fa_u=find(edge[i].u),fa_v=find(edge[i].v);
		if(fa_u!=fa_v){
			unionn(edge[i].u,edge[i].v);
			ans+=edge[i].l;
		}
	}
	cout<<ans;
	return 0;
} 

p.s.以后这种问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值