加超级源点的Kruskal算法

题目描述

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

解题思路

首先依然是对题目的理解,假如没有“天上来”的操作,那么就是一个简单的最小生成树问题,使用Kruskal算法就可以求解。
但是此题并不是那么简单,黄河之水可以天上来,那么每块田就多出了一个来水的渠道。既然如此,可以把黄河水看成一个新的节点,然后对所有的点使用Kruskal算法求解即可。
Kruskal算法:将所有的边按照从小到大的顺序排序,依次取出最小的边,判断是否可以和已选出的边形成回路(利用并查集),若不会则选择该边,并将该边两个端点所在集合合并。共找出n-1条边即可。

代码

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=310;//田个数的最大值  
const int maxm=90000;//边个树的最大值 
struct Edge
{
	int u,v,w;//出发点,到达点,权值 
}Edges[maxm];
int tot=0;//边的数目 
bool cmp(Edge x,Edge y)
{
	return x.w<y.w;
}
int par[maxn],rnk[maxn];
void init(int n) { for(int i=0;i<n;i++) par[i]=i, rnk[i]=1; }
int find(int x) { return par[x]==x?x:par[x]=find(par[x]); }
bool unite(int x,int y)
{
	x=find(x),y=find(y);
	if(x==y) return false;
	
	if(rnk[x]>rnk[y]) swap(x,y);
	par[x]=y;
	rnk[x]+=rnk[y];
	rnk[y]=rnk[x];
}
int main()
{
	int n,weight;//点的个数和边的权值 
	cin>>n;//农田个数 
	init(n+1);//初始化并查集 
	for(int i=1;i<=n;i++)
    {
    	Edges[tot].u=0;
    	Edges[tot].v=i;
    	cin>>Edges[tot].w;
    	tot++;
    }
    for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			cin>>weight;
			if(j>i) 
			{
                Edges[tot].u=i;
    	        Edges[tot].v=j;
    	        Edges[tot].w=weight;
    	        tot++;
		    }
		}
    } 
    sort(Edges,Edges+tot,cmp);
    int start=0;//边集种选的边 
    int mp=0;//消耗的法力值 
    for(int i=0;i<n;i++)
    {
    	while(find(Edges[start].u)==find(Edges[start].v))//按照权值由小到大的顺序找到第一个不成环的一条边 
            start++;
        unite(Edges[start].u,Edges[start].v);
        mp+=Edges[start].w;
        start++; 
	}
	cout<<mp<<endl;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值