题目:
瓜农王大爷去年种西瓜赚了不少钱。看到收入不错,今年他又重新开辟了n个西瓜地。
对于每个西瓜地他可以选择在本地打井,也可以修管道从另一个瓜地将水引过来,当然打井和修管道的费用有差别。
已知在第i个西瓜地打井需要耗费w[i]元,在第i、j个西瓜地之间修管道需要耗费p[i][j]元。
王大爷要想使所有瓜地都被浇上水,至少需要花费多少钱(打井与修管道的费用和)?求出最小费用。
分析:
可以设置0为起始点,0至i为各个西瓜地打井的费用,i、j之间为修管道的费用,这样就可以使用kruskal算法了。
代码:
//最小生成树Kruskal算法
#include<stdio.h>
#include<algorithm>
using namespace std;
int tree[100];//双亲表示法
int p[100]; //点的权值
struct edge{ //边表
int from;
int to;
int cost;
}e[1000];
int find(int x){ //并查集
if(tree[x]==-1) return x;
else{
int tmp=find(tree[x]);
tree[x]=tmp; //路径压缩
return tmp;
}
}
bool cmp(edge a,edge b){ //按照边值从小到大排序
return a.cost < b.cost;
}
int main(){
int num;
scanf("%d",&num);
tree[0]=-1;
for(int i=1;i<=num;i++){ //写入点权值+初始化tree数组
scanf("%d",&p[i]);
tree[i]=-1;
}
int tmp,cnt=0;
for(int i=1;i<=num;i++){
for(int j=1;j<=num;j++){ //写入边权值
if(i>=j){
scanf("%d",&tmp);
continue;
}
else{
scanf("%d",&e[cnt].cost);
e[cnt].from=i;
e[cnt++].to=j;
}
}
}
for(int i=1;i<=num;i++){ //点权值转化为边权值,放入边集 重点!
e[cnt].from=0;
e[cnt].to=i;
e[cnt].cost=p[i];
cnt++;
}
sort(e,e+cnt,cmp); //边权值排序
//基于kruskal的最小生成树算法
int sum=0;
for(int i=0;i<cnt;i++){
int a=find(e[i].from);
int b=find(e[i].to);
if(a!=b){ //如果结点根不同,则说明不连通,该边可以加入
tree[a]=b;
sum+=e[i].cost;
}
}
printf("%d\n",sum);
}