【魔法东东】最小生成树

题目

题意
东东在老家农村无聊,想种田。农田有 n 块,编号从 1~n。种田要灌溉,众所周知东东是一个魔法师,他可以消耗一定的 MP 在一块田上施展魔法,使得黄河之水天上来。他也可以消耗一定的 MP 在两块田的渠上建立传送门,使得这块田引用那块有水的田的水。 (1<=n<=3e2)
黄河之水天上来的消耗是 Wi,i 是农田编号 (1<=Wi<=1e5)
建立传送门的消耗是 Pij,i、j 是农田编号 (1<= Pij <=1e5, Pij = Pji, Pii =0)
求东东为所有的田灌溉的最小消耗
Input
第1行:一个数n
第2行到第n+1行:数wi
第n+2行到第2n+1行:矩阵即pij矩阵
Output
东东最小消耗的MP值
Example Input
4
5
4
4
3
0 2 2 2
2 0 3 3
2 3 0 4
2 3 4 0
Example Output
9

题目大意

本题给出图中的n个节点,同时给出两种操作:1、在两个节点间连接一条边,使得两个节点都有水,边权已给出;2、消耗 Wi 为第 i 个节点供水,题目要求用以上两种操作,使得消耗最小且每个节点都有水。实质上可以转化为最小生成树问题。

解题思路

本题明显是一道最小生成树问题,但是不好处理每个节点上的“黄河之水天上来”问题。此时只需要添加一个 0 号节点即可,Wi 可以理解为是 0 号节点与 i 号节点之间的边权。之后再使用Kruskal算法找到最短路径长度即可。
此处的Kruskal算法借助并查集实现。首先将所有边按权值由小到大排序,可以借助小根堆实现。随后每次取边,若这条边的头尾节点不在一个并查集内,就可以选用这条边,否则不选。由于本题添加了节点 0 ,所以当选定边数为 n 条是就可以结束循环了,求出选定边权总和就是最小消耗值。
本题代码可以用于存储Kruskal算法模板,效果较好。

具体代码

#include <iostream>
#include <queue>
#define MAXN 100005

using namespace std;

struct road{
	int from;
	int to;
	int length;
	bool operator<(const road& a) const
    {
        return length > a.length;      //小根堆,反号大根堆 
    }
}; 

priority_queue<road> a;     //自定数据类型优先队列 
int fa[305];

int getfa(int n)
{
	if(fa[n] != n)
	{
		fa[n] = getfa(fa[n]);
	}
	return fa[n];
}

void unity(int x, int y)
{
	int f1 = getfa(x);
	int f2 = getfa(y);
	fa[f1] = f2;
}

int main(int argc, char** argv) 
{ 
	for(int i = 0; i < 305; i++)
	{
		fa[i] = i;
	}
	int n,cnt = 0,ans = 0;
	road temp;
	cin >> n;
	for(int i = 1; i <= n; i++)
	{
		cin >> temp.length;
		temp.from = 0;
		temp.to = i;
		a.push(temp);
	}
	for(int i = 1; i <= n; i++)
	{
		for(int j = 1; j <= n; j++)
		{
			cin >> temp.length;
			temp.from = i;
			temp.to = j;
			if(i < j)
			{
				a.push(temp);
			}
		}
	}
	while(cnt < n)
	{
		temp = a.top();
		a.pop();
		if(getfa(temp.from) != getfa(temp.to))
		{
			ans += temp.length;
			unity(temp.from,temp.to);
			cnt++;
		}
	}
	cout << ans;
	return 0;
}```

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值