【题目描述】
发展采矿业当然首先得有矿井,小 FF 花了上次探险获得的千分之一的财富请人在岛上挖了 n 口矿井,但他似乎忘记考虑的矿井供电问题……
为了保证电力的供应,小 FF 想到了两种办法:
在这一口矿井上建立一个发电站,费用为 v(发电站的输出功率可以供给任意多个矿井)。
将这口矿井与另外的已经有电力供应的矿井之间建立电网,费用为 p。
小 FF 希望身为「NewBe_One」计划首席工程师的你帮他想出一个保证所有矿井电力供应的最小花费。
【输入】
第一行一个整数 n,表示矿井总数。
第 2∼n+1 行,每行一个整数,第 i 个数 vi 表示在第 i 口矿井上建立发电站的费用。
接下来为一个 n×n 的矩阵 p,其中 pi,j 表示在第 i 口矿井和第 j 口矿井之间建立电网的费用(数据保证有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
分析:题目只说了一个方案,明显的最小生成树算法。
第二个方案是可以建多个电站进行供电,最终考察的是第二个方案。因此要使用改进的prim算法。算法改进地方——不是选择最小边,二是选择最小边或最小建站花费。
#include <iostream>
#include <iomanip>
#include <math.h>
#include <string>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;/**< 最小生成树问题 */
int n,f[305][305],d[305],v[305],mini[305];
int getMin()
{
int i,minc=99999999,mi=0;
for(i=1; i<=n; i++)
{
if(v[i]==0&&mini[i]<minc)
{
minc=mini[i];
mi=i;
}
}
return mi;
}
int getMin1()
{
int i,minc=99999999,mi=0;
for(i=1; i<=n; i++)
{
if(v[i]==0&&d[i]<minc)
{
minc=d[i];
mi=i;
}
}
return mi;
}
int main()
{
int i,j,k,k1,sum=0,mindianzhan=99999999,mi=1;
memset(v,0,sizeof(v));
memset(mini,127/3,sizeof(mini));
cin>>n;
for(i=1; i<=n; i++) /**< 求出花费最小的电站,贪心的角度看这里一定会建设电站*/
{
cin>>d[i];
if(d[i]<mindianzhan)
{
mindianzhan=d[i];
mi=i;
}
}
for(i=1; i<=n; i++)
{
for(j=1; j<=n; j++)
{
cin>>f[i][j];
}
}
/**< 只求最小生成树50分 选择连接电网和建新电站中较小的方案, sum+=min(mini[k],d[k])70分*/
/**< Prim算法选择最小边的时候,在最小边和最小建站花费中选择最小的,因为目标是供电,不一定要全部连起来 */
mini[mi]=0;
for(j=1; j<=n; j++)
{
k=getMin();/**< 最小边 */
k1=getMin1();/**< 最小建站花费 */
if(mini[k]<d[k1])
{
sum+=mini[k];
v[k]=1;
for(i=1; i<=n; i++)
{
mini[i]=min(mini[i],f[k][i]);
}
}
else
{
v[k1]=1;
sum+=d[k1];
for(i=1; i<=n; i++)
{
mini[i]=min(mini[i],f[k1][i]);
}
}
}
cout<<sum+d[mi];/**< 别忘了加上prim算法里面第一个电站花费 */
return 0;
}