题目背景
John的农场缺水了!!!
题目描述
农民John 决定将水引入到他的n(1<=n<=300)个牧场。他准备通过挖若干井,并在各块田中修筑水道来连通各块田地以供水。在第i 号田中挖一口井需要花费W_i(1<=W_i<=100,000)元。连接i 号田与j 号田需要P_ij (1 <= P_ij <= 100,000 , P_ji=P_ij)元。
请求出农民John 需要为连通整个牧场的每一块田地所需要的钱数。
输入输出格式
输入格式:
第1 行为一个整数n。
第2 到n+1 行每行一个整数,从上到下分别为W_1 到W_n。
第n+2 到2n+1 行为一个矩阵,表示需要的经费(P_ij)。
输出格式:
只有一行,为一个整数,表示所需要的钱数。
输入输出样例
输入样例#1:
4
5
4
4
3
0 2 2 2
2 0 3 3
2 3 0 4
2 3 4 0
输出样例#1:
9
思路
最小生成树
需要注意的是不一定建一棵树,因为我们可以多棵树,挖多个井;
处理方法:设置一个超级原点,将各个井与超级原点连边,边权为打井所需花费,然后构建最小生成树。
证明:将打井费用作为边权,这样在做最小生成树时,就能直接选择打井(与超级原点相连)还是连边(与已打井相连),输出答案为所有边权。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=310*310;
struct node{
int x;int y;int v;
}tu[maxn];
int n,ans,num,tot,a;
int fa[400],cost[maxn];
bool cmp(node c,node d){
return c.v<d.v;
}
int father(int x){
if(fa[x]==x) return x;
return fa[x]=father(fa[x]);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&cost[i]);
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
scanf("%d",&a);{
if(i!=j){
tu[++tot].v=a;
tu[tot].x=i;tu[tot].y=j;
}
else {
tu[++tot].v=cost[i];//与超级原点连边
tu[tot].x=0;
tu[tot].y=i;
}
}
}
for(int i=0;i<=n;i++)
fa[i]=i;
sort(tu+1,tu+tot+1,cmp);
for(int i=1;i<=tot;i++){
int xx=father(tu[i].x);int yy=father(tu[i].y);
if(xx!=yy){
ans+=tu[i].v;
fa[xx]=yy;
num++;
}
if(num==n) break;
}
printf("%d",ans);
return 0;
}