题目
东东在老家农村无聊,想种田。农田有 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最短路径
实现:根据题目意思,我们可以引入一个0号土地,0号土地引入到i号土地对应的消耗能量等于东东黄河之水天上来消耗的能量,此时这个题转换为一个单纯的最短路径问题
·根据消耗的能量对边进行排序(从小到大)
·从消耗能量值最小的边开始
·若加入该边后,出现环,则扔掉该边
·若不成环,将边加入图中,更新ans+=term[j].value
一行实现并查集查找操作:
int find(int x) {return par[x]==x?x:par[x]=find(par[x]);}
代码实现
#include<cstdio>
#include<algorithm>
using namespace std;
int n,ans,num,cnt;
struct land
{
int l,r,value;
bool operator<(const land &p) const
{
return value<p.value;
}
}term[1000005];
int rnk[10005],par[10005];
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[y]=(rnk[x]+=rnk[y]);
return true;
}
int main()
{
scanf("%d",&n);
int temp;
for(int i=1;i<=n;i++)
{
scanf("%d",&term[i].value);
term[i].l=0;
term[i].r=i;
}
num=n+1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
scanf("%d",&temp);
if(j>i)
{
term[num].l=i;
term[num].r=j;
term[num].value=temp;
num++;
}
}
}
sort(term+1,term+num);
for(int i=0;i<=n;i++)
{
par[i]=i;
rnk[i]=1;
}
for(int j=1;j<num;j++)
{
int l=term[j].l;
int r=term[j].r;
if(unite(l,r))
{
ans+=term[j].value;
cnt++;
if(cnt==n) break;
}
}
printf("%d",ans);
}