思路
虚拟出一个超级源点
题意所求转换为将所有的n+1个点联通
这样无论是
方案1:挖一口井花费 W(i) 元
还是方案2:连接 i 号点与 j 号点 花费 P(i,j) 元
都转换为连一条边到超级源点的花费 花费W(i)元或P(i,j)元
本质上是将 花费W(i)元 转换为 花费P(i,超级源点)元
这样就可以用最小生成树算法解决此题
由于本题输入图的格式是邻接矩阵 考虑使用Prim算法
AC代码
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int N = 310;
typedef pair<int, int>PII;
priority_queue<PII, vector<PII>, greater<PII>>h;
int w[N][N];//存图 邻接矩阵
int dist[N];
bool st[N];
int n;
//Prim最小生成树算法
int prim()
{
//初始化所有距离为正无穷
//dist[i]表示i点到当前生成树的最短距离
memset(dist, 0x3f, sizeof dist);
dist[0] = 0;
h.push(make_pair(dist[0], 0));
int res = 0;//累计最小生成树的总权值
while (h.size())
{
PII t = h.top();
h.pop();
//如果已经考虑过了 就跳过
if (st[t.second])continue;
//要考虑
st[t.second] = true;//标记已经被考虑了
res += t.first;//权值累加
for (int i = 0; i < n + 1; i++)
{
//没有这条边就跳过
if (!w[t.second][i])continue;
int u = i, v = w[t.second][i];
//考虑过这条边 也跳过
if (st[u])continue;
if (dist[u] > v)
{
dist[u] = v;
h.push(make_pair(dist[u], u));
}
}
}
return res;
}
int main()
{
//将每一个点连向超级源点
//不妨设超级源点编号为0
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf("%d", &w[0][i]);
w[i][0] = w[0][i];//无向图
}
//读入邻接矩阵
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
scanf("%d", &w[i][j]);
printf("%d\n", prim());
return 0;
}