题目
题目描述
发展采矿业当然首先得有矿井,小 FF 花了上次探险获得的千分之一的财富请人在岛上挖了 口矿井,但他似乎忘记考虑的矿井供电问题……
为了保证电力的供应,小 FF 想到了两种办法:
在这一口矿井上建立一个发电站,费用为
(发电站的输出功率可以供给任意多个矿井)。
将这口矿井与另外的已经有电力供应的矿井之间建立电网,费用为
。
小 FF 希望身为「NewBe_One」计划首席工程师的你帮他想出一个保证所有矿井电力供应的最小花费。
输入格式
第一行一个整数 ,表示矿井总数。
第 行,每行一个整数,第 个数 表示在第 口矿井上建立发电站的费用。
接下来为一个 的矩阵 ,其中 表示在第 口矿井和第 口矿井之间建立电网的费用(数据保证有 ,且 )。
输出格式
输出仅一个整数,表示让所有矿井获得充足电能的最小花费。
样例
样例输入
4
5
4
4
3
0 2 2 2
2 0 3 3
2 3 0 4
2 3 4 0
样例输出
9
样例解释
小 FF 可以选择在 号矿井建立发电站然后把所有矿井都不其建立电网,总花费是 。
题解
拿到这道题,直观的想法是首先对原图进行一边最小生成树,然后选取建发电站费用最小的矿井建发电站,与剩余矿井形成电网
然鹅这样并不行,因为我们可以建立多个互不相连的电网,每个电网有一个发电站,因为有可能有两个相离极远的矿井群,如果强行连接它们花费将灰常大,不如在两边各建一个发电站形成两个电网,也即跑一个最小生成森林,然后再每一个森林中建立发电站,但问题来了,我们并不知道哪些点一个森林,这就要用到一个处理森林的常用技巧:添加一个虚拟节点,让所有森林形成一棵树,而这样我们还顺便解决了贪心遍历的问题:直接把虚拟节点与各个节点的距离设为在这个节点上建立发电厂的费用即可
当然我们也可以这样理解:
建立发电厂其实是从一个一个强大的拥有电的矿坑取电(那个矿坑专门挖矿后发电),开销为到强大矿坑的距离,然后为了让所有矿坑用上电,就需要让所有矿坑(加上供电矿坑)形成电网
代码
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=305;
const int MAXM=1000005;
struct edge{
int u,v,w;
}G[MAXM];
int fa[MAXN];
int get(int x){
if(fa[x]==x){
return x;
}
return fa[x]=get(fa[x]);
}
bool cmp(edge x,edge y){
return x.w<y.w;
}
int kruskal(int n,int m){
int sum=0;
sort(G+1,G+1+m,cmp);
for(int i=1;i<=m;i++){
int fu=get(G[i].u);
int fv=get(G[i].v);
if(fu!=fv){
fa[fv]=fu;
sum+=G[i].w;
}
}
return sum;
}
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
fa[i]=i;
}
int a;
int m=0;
for(int i=1;i<=n;i++){
scanf("%d",&a);
m++;
G[m].u=0;
G[m].v=i;
G[m].w=a;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
scanf("%d",&a);
m++;
G[m].u=i;
G[m].v=j;
G[m].w=a;
}
}
printf("%d",kruskal(n,m));
return 0;
}