题目
题目描述
发展采矿业当然首先得有矿井,小 FF 花了上次探险获得的千分之一的财富请人在岛上挖了 n 口矿井,但他似乎忘记考虑的矿井供电问题……
为了保证电力的供应,小 FF 想到了两种办法:
- 在这一口矿井上建立一个发电站,费用为 v(发电站的输出功率可以供给任意多个矿井)。
- 将这口矿井与另外的已经有电力供应的矿井之间建立电网,费用为 p。
小 FF 希望身为「NewBe_One」计划首席工程师的你帮他想出一个保证所有矿井电力供应的最小花费。
输入格式
第一行一个整数 n,表示矿井总数。
第
2
∼
n
+
1
2\sim n+1
2∼n+1 行,每行一个整数,第 i 个数 v_i 表示在第 i 口矿井上建立发电站的费用。
接下来为一个
n
×
n
n\times n
n×n 的矩阵 p,其中 p_{i,j} 表示在第 i 口矿井和第 j 口矿井之间建立电网的费用(数据保证有
p
i
,
j
=
p
j
,
i
,且
p
i
,
i
=
0
p_{i,j}=p_{j,i},且 p_{i,i}=0
pi,j=pj,i,且pi,i=0)。
输出格式
输出仅一个整数,表示让所有矿井获得充足电能的最小花费。
样例
输入
4
5
4
4
3
0 2 2 2
2 0 3 3
2 3 0 4
2 3 4 0
输出
9
思路
这个题很妙啊
一眼贪心
先选择建发电站费用最低的矿井,然后再在所有矿井建立电网
但是,因为没有考虑到多个矿井的情况,所以不对
所以,我们要释放大脑,再读题
我们发现,这道题需要求的是最低费用,又要在各个矿井之间建电网,所以,这肯定是一道最小生成树题,但是现在的题目是无法使用最小生成树的,所以我们要转化
在这个图中建一个标号为0的虚拟结点,而每个矿井与这个虚拟结点都有一条边,这条边的权值就是在每个矿井建发电站的费用
好,这道题已经转化成了这个样子:
现在有一个供电厂(虚拟结点0),求能把所有矿井都供上电的最低费用
现在这道题已经很明显是一个最小生成树题了
再拿出刘汝佳の超级Kruskal,不难发现,这道题非常简单就AC了
超级优美,超级完美的代码
ACcode
#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
const int maxn=90005;
int minv=INF;
int n,m;
int ans;
int p[maxn];
int u[maxn],v[maxn],r[maxn],w[maxn];
int find(int x) { return p[x]==x?x:p[x]=find(p[x]); }
struct cmp{ bool operator ()(const int &i,const int &j) { return w[i]<w[j]; } };
void Kruskal(){
sort(r+1,r+1+m,cmp());
for(int i=1;i<=m;i++){
int e=r[i];int x=find(u[e]),y=find(v[e]);
if(x!=y) p[x]=y,ans+=w[e];
}
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);//关一下流同步
cin>>n;
for(int i=1;i<=n;i++){
p[i]=i;
int ww;cin>>ww;
u[++m]=i,v[m]=0,w[m]=ww,r[m]=m;//连发电厂只需要一条有向边(想一想,为什么)
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
int ww;cin>>ww;
if(ww) u[++m]=i,v[m]=j,w[m]=ww,r[m]=m;//每一条边
}
Kruskal();
cout<<ans;
return 0;
}