掌握魔法の东东 I 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

解题思路
1.两个田之间的边有权,是pij
我们要选择一种消耗最小的办法,很明显是一道最小生成树的问题。

2.问题在于黄河之水天上来,所以我们采用一种办法,建立一个点,叫做源点,这点是用于黄河之水天上来的。意思就是,他和每一个点的边的权重,就是Wi。

3.这样,我们就可以生成最小生成树了。

代码实现

#include<iostream>
#include<algorithm>
#include<vector> 
using namespace std;
//采用邻接表 装点
//用并查集判断是否形成环路 
//令0点为原点,黄河之水天上来 
const int Max = 100000;
const int Max_n = 50000;
//图 
struct edge{
	int u,v,w;
	bool operator<(const edge &e )
	{
		if(w!=e.w) return w<e.w;
		else if(u!=e.u )  return u<e.u;
		else  return v<e.v;
	}
};
edge e[Max];int ei;   //装所有边的 
void addE( int u,int v,int w )
{
	edge E1;
	E1.u = u,E1.v=v,E1.w=w;
	e[ei] = E1;ei++;
}
//并查集
int par[Max_n];  //老大 
int men[Max_n];  //这个组有多少人 
int find(int x)
{
	if(par[x] == x) return x;
	else  par[x] = find(par[x]);
	return par[x];
}
bool unite(int x,int y)
{
	x = find(x),y = find(y);
	if( x==y ) return 0;
	if( men[x] > men[y] )  par[y] = par[x],men[x]+=men[y];
	else par[x] = par[y] , men[y]+=men[x];
	
	return 1;
}
void ini(int n)
{
	for(int i=0;i<=n;i++)
	{par[i] = i;men[i] =1;}
}
//
int n;
int wi;
int main()
{
	ios::sync_with_stdio(0);
	cin>>n;
	ini(n);
	for(int i=1;i<=n;i++)  //0号点设为天 ,黄河之水天上来相当于
	//0号点到各个点的路径长度 
	{
		cin>>wi;
		addE(i,0,wi);
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)   
		{
			cin>>wi;
			addE(i,j,wi);
		}
	}
	sort(e,e+ei);
	int sum=0;
	int cn=0;
	for(int i=0;i<ei;i++)
	{
		if(unite(e[i].u,e[i].v))
		sum = sum + e[i].w;
	}
	cout<<sum<<endl;
	return 0;
}

小结
采用克鲁斯卡尔算法,对边集进行的操作。
踩了一个坑,是关于sort的,我的operator< 只判断了w,结果导致我出错了。对于所有的u,v,w,虽然我们只判断w,但是w相等时,u,v该指定一种排序。

发布了31 篇原创文章 · 获赞 1 · 访问量 1045

猜你喜欢

转载自blog.csdn.net/Hanpi_learnc/article/details/105244133