题目描述
发展采矿业当然首先得有矿井,小 FF 花了上次探险获得的千分之一的财富请人在岛上挖了 nnn 口矿井,但他似乎忘记考虑的矿井供电问题……
为了保证电力的供应,小 FF 想到了两种办法:
- 在这一口矿井上建立一个发电站,费用为 vvv(发电站的输出功率可以供给任意多个矿井)。
- 将这口矿井与另外的已经有电力供应的矿井之间建立电网,费用为 ppp。
小 FF 希望身为「NewBe_One」计划首席工程师的你帮他想出一个保证所有矿井电力供应的最小花费。
输入格式
第一行一个整数 nnn,表示矿井总数。
第 2∼n+12\sim n+12∼n+1 行,每行一个整数,第 iii 个数 viv_ivi 表示在第 iii 口矿井上建立发电站的费用。
接下来为一个 n×nn\times nn×n 的矩阵 ppp,其中 pi,jp_{i,j}pi,j 表示在第 iii 口矿井和第 jjj 口矿井之间建立电网的费用(数据保证有pi,j=pj,ip_{i,j}=p_{j,i}pi,j=pj,i,且 pi,i=0p_{i,i}=0pi,i=0)。
输出格式
输出仅一个整数,表示让所有矿井获得充足电能的最小花费。
思路:感觉这题建立超级源点的方法挺巧妙的,以前没有碰到过,所以记录一下。这道题如果吧超级源点到每个矿井连边,边权为建发电站的费用,可以看出,我们需要的是最小的边权和,这就成了最小生成树的模型,n只有300,跑一边prim就行了。
代码:
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #define ll long long int using namespace std; const int INF=0x3f3f3f3f; int n,val[305],p[305][305],ans,col[305],dis[305]; void prim(){ for(int i=1;i<=n-1;i++) dis[i]=p[n][i]; p[n][n]=INF; dis[n]=0;col[n]=1; for(register int i=1;i<=n-1;i++){ int minn=INF,k; for(int j=1;j<=n;j++) if(!col[j]&&minn>dis[j]){ minn=dis[j]; k=j; } ans+=dis[k]; col[k]=1; for(int j=1;j<=n;j++) dis[j]=min(dis[j],p[k][j]); } } int main(){ scanf("%d",&n); n++; for(int i=1;i<=n-1;i++){ scanf("%d",&p[n][i]); p[i][n]=p[n][i]; } for(int i=1;i<=n-1;i++) for(int j=1;j<=n-1;j++) scanf("%d",&p[i][j]); for(int i=1;i<=n-1;i++){ p[i][i]=INF; dis[i]=INF; } prim(); printf("%d",ans); return 0; }