Week6 作业C - 掌握魔法の东东 I

1.题意

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

2.样例

Input

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

Output

东东最小消耗的MP值

Sample Input

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

Sample Output

9

3.解题思路

  1. 如果没有“黄河之水天上来”则该题为最小生成树问题,因此考虑将该图加一个超级原点0,且该点与其他点的边的权重为wi,就转化为对这n个点求最小生成树问题
  2. 求最小生成树考虑Kruskal算法,每次贪心地尝试将图中最小的边标记为树边,非法则跳过。
      用一边结构体存储图的边的两个顶点和边的权重,用vector存储,将边按照权重大小排序,每次取出最小边,用数组vis标记边的点是否出现过,若未出现过则将其标记且初始化sum=1,用并查集的查找操作,若边的两个顶点不属于同一集合则表示合法将其权重求和即可,直到vector长度为0或生成树的边等于n跳出循环即求得最小生成树
  3. 该题最重要的还是将题意转化为求解最小生成树,注意观察规律

4.AC代码

#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
const int M=3e2+5;
struct edg{
	int u;
	int v;
	int w;
	edg(int a,int b,int c){
		u=a;
		v=b;
		w=c;
	}
	bool operator<(const edg &e)const{
		return w>e.w;
	}
	bool operator!=(const edg &e)const{
		return u!=e.u||v!=e.v||w!=e.w;
	}
};
vector<edg>e;
int pa[M];
int sum[M];
int vis[M];
void swap(int &x,int &y)
{
	int temp=x;
	x=y;
	y=temp;
}
int find(int x)
{
	if(pa[x]==x) return x;
	return pa[x]=find(pa[x]);
}
bool unite(int x,int y)
{
	x=find(x);
	y=find(y);
	if(x==y) return false;
	if(sum[x]>sum[y]) swap(x,y);
	pa[x]=y;
	sum[x]+=sum[y];
	sum[y]=sum[x];
	return true;
}
int main()
{
	int n,w;
	cin>>n;
	memset(vis,0,sizeof(vis));
	pa[0]=0;
	for(int i=1;i<=n;i++)
	{
		pa[i]=i;
		cin>>w;
		e.push_back(edg(0,i,w));
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			cin>>w;
			e.push_back(edg(i,j,w));
		}
	}
	sort(e.begin(),e.end());
	int k=0,count=0;
	while(e.size()>0&&k<n)
	{
		edg ed=e[e.size()-1];
		e.pop_back();
		int u=ed.u;
		int v=ed.v;
		int w=ed.w;
		int a=find(u);
		int b=find(v);
		if(vis[u]==0)
		{
			vis[u]=1;
			sum[u]=1;
		}
		if(vis[v]==0)
		{
			vis[v]=1;
			sum[v]=1;
		}
		if(a!=b)
		{
			k++;
			count+=w;
			unite(u,v);
		} 
	}
	cout<<count<<endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值