第六周作业——掌握魔法の东东

掌握魔法の东东

一、题目
  东东在老家农村无聊,想种田。农田有 n 块,编号从 1~n。种田要灌氵
  众所周知东东是一个魔法师,他可以消耗一定的 MP 在一块田上施展魔法,使得黄河之水天上来。他也可以消耗一定的 MP 在两块田的渠上建立传送门,使得这块田引用那块有水的田的水。 (1<=n<=3e2)黄河之水天上来的消耗是 Wi,i 是农田编号 (1<=Wi<=1e5),建立传送门的消耗是 Pij,i、j 是农田编号 (1<= Pij <=1e5, Pij = Pji, Pii =0)。
  求东东为所有的田灌氵的最小消耗。

二、输入
  第1行:一个数n
  第2行到第n+1行:数wi
  第n+2行到第2n+1行:矩阵即pij矩阵

三、输出
  东东最小消耗的MP值

四、样例输入输出

Input

4
5
4
4
3
0 2 2 2
2 0 3 3
2 3 0 4
2 3 4 0

Output

9

五、解题思路
  并查集思想+Kruskal算法。
  两块农田之间建立传送门可以抽象乘加权无向边,而天上引水可以抽象成一个点,该点到其他点均有加权无向边。将这两种边存入自定义类型的边数组,并根据权值排序。逐个选择权值最小的边,将两个点所在的集合合并,若能合并,则二者原本不连通,可以添加该边,若不能合并,则说明二者原来就是连通的,添加该边后,构成环路,不符合规则,所以不添加。每添加一条边,则加上该边的权值,直到遍历完所有的边。
  注意:输入建立传送门消耗的数组时,因为数组是对称的,所以存一半的边即可。所有的点均合并为一个集合后应该就可以结束循环,不用遍历完所有边,可以继续优化,不过样例代码未体现。

六、样例代码

#include<iostream>
#include<algorithm>
#include<stdio.h>
using namespace std;

int n,m;
int a[50005];
int ans=0;
struct edge
{
	int u,v,w;
	edge()
	{
		u=-1;v=-1;w=-1;
	}
	void in(int uu,int vv,long long int ww)
	{
		u=uu;v=vv;w=ww;
	}
	bool operator < (const edge& e)
	{
		return w<e.w;
	}
};

edge e[100005];

int find(int x){
	if(a[x] == x)
		return x;
	a[x] = find(a[x]);
	return a[x];	
}

bool unite(int x,int y)
{
	x=find(x);
	y=find(y);	
	if(x==y) return false;

	a[x]=y;
	return true;	
} 


int main()
{
	cin>>n;
    int w;
	int k=0;//边数 
	for(int i=0;i<=n;i++) a[i] = i;
	
	for(int i=1;i<=n;i++)
	{
		cin>>w;
		e[k].in(0,i,w);
		k++; 
	} 
	
    int p;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
		    cin>>p;
			if(i>j)
			{
		    	e[k].in(i,j,p);
			    k++;				
			}
		}
	}
	sort(e,e+k);
 
	for(int i=0;i<k;i++)
	{
		if(unite(e[i].u,e[i].v))
		{
			ans=ans+e[i].w;
		}
	}
	printf("%d\n",ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值