c++生成树模板题——掌握魔法の东东

题意

东东在老家农村无聊,想种田。农田有 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

Output

9

思路

如果,没有黄河之水天上来,那么我们需要找的就是所有连接所有田的最短路,即最小生成树。
加上所有黄河之水天上来,我们可以理解为,多了一个田,这个田在一个特殊的地方,它可以与所有的田连接。所以,我们现在的任务就是,找加上这一个特殊田之后的最小生成树。

代码:

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
//并查集 算法需要利用并查集判断 最小生成树是否构成了圈
int par[90005];
void ini(int n)
{
	for(int i=0; i<=n; i++)
	{
		par[i]=i;
	}
}
int find(int x)
{
	if(par[x]==x)
		return x;
	return par[x]=find(par[x]);
}
bool unite(int x,int y)
{
	x=find(x);
	y=find(y);
	if(x==y) return false;
	par[x]=y;
	return true;
}

// Kruskal
struct edge
{
	int v,u,w;
	bool operator < (const edge &e) const
	{
		return w<e.w;
	}
};
edge map[90005];
int tot=1;   //边的索引
int n;  
int sum=0;  //最小生成树的所有边的权值和

void Kruskal()
{
	int k=0;
	for(int i=1;i<=tot;i++)
	{
		if(unite(map[i].v,map[i].u))   //判断是否构成圈 否则加入
		{
			k++;
			sum+=map[i].w;
		}
		if(k==n) return ;   //k为当前边数 最小生成树的边为点-1
	}
}

int main()
{
	scanf("%d",&n);
	int w;
	for(int i=1; i<=n; i++)
	{//将特殊点 与所有的已有点连接 并赋权值
		scanf("%d",&w);
		map[tot].v=0;
		map[tot].u=i;
		map[tot].w=w;
		tot++;
	}
	for(int i=1; i<=n; i++)
	{
		for(int j=1; j<=n; j++)
		{   //存边
			scanf("%d",&w);
			if(w==0) continue;
			map[tot].v=i;
			map[tot].u=j;
			map[tot].w=w;
			tot++;
		}
	}
	tot--;
	sort(map+1,map+tot+1);   //按权值排序
	ini(n);  //初始化并查集
	Kruskal();   //最小生成树算法
	cout<<sum;
	return 0;
}

总结

最小生成树模板题。

发布了28 篇原创文章 · 获赞 1 · 访问量 1233
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 游动-白 设计师: 上身试试

分享到微信朋友圈

×

扫一扫,手机浏览