思路:
由题意可知,每个城市通电的话有两种方法:一种是用电线连接,另一种是采用建立发电站连接。而假设只有用电线连接的话,要使得花费最小,那么这就很明显是采用我们的最小生成树算法进行求解。但问题是还存在建立发电站供电的要求,建立了发电站你才能够发电啊,此时连接两座城市的电线才有效啊。所以前提是先考虑建立发电站的情况。
另外就是,最后的图可能不是一个连通块(因为只要每个连通块内都包含一个发电站即可。),而是多个连通块的情况,那么若是多个连通块,那么每个连通块都最少有一个发电站。然后要我们求最小花费。所以这里该做一个变形:如果一个城市建立电站的话,那么我们可以建立一个虚拟源点,然后每座城市都向源点连接一条边,边权是:该城市建立发电站的花费。从而枚举所有的情况。 这是等价的在原问题里的村庄建立发电站和在转化后的问题里建立一个虚拟源点,建立发电站的花费等价,是能一一对应的。所以可以转化问题。
建立虚拟源点0号点,使得0号点与每个城市都连通。然后
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII; //记录坐标!
typedef long long LL;
const int N = 2e3 + 10;
PII q[N]; //记录每个城市的坐标
int wc[N], wk[N]; //分别表示每座城市的电站花费和电线花费!
int n;
LL dist[N]; //dist数组表示该点距离生成树集合的最短距离。
bool st[N]; //prim算法还需开一个bool数组表示每个点是否在生成树集合种。
vector<int> ans1; //存储在哪些点建立电站。
vector<PII> ans2; //哪两个点之间建立电线
int fa[N]; //存储记录每个点的父节点前驱节点。
LL get_cost(int a, int b)
{
int dx = abs(q[a].x - q[b].x);
int dy = abs(q[a].y - q[b].y);
return (dx + dy)*1ll*(wk[a]+wk[b]);
}
LL prim()
{
memset (dist, 0x3f, sizeof (dist));
dist[0] = 0;
st[0] = true; //本题中虚拟源点是已经确立了答案,即在集合中了
for (int i=1; i <= n; i ++) dist[i] = wc[i]; //每个点到自己的距离
LL res = 0; //表示我们的总代价是多少。
for (int i=0; i < n; i ++)
{
int t = -1;
for (int j=1; j <= n; j ++)
if (!st[j] && (dist[j] < dist[t] || t == -1))
t = j;
res += dist[t];
st[t] = true;
if (!fa[t]) ans1.push_back(t);
else ans2.push_back({fa[t], t});
for (int j=1; j <= n; j ++)
if (dist[j] > get_cost(t, j)) //如果在j点建立电站的花费 > 两点之间的建立电线的花费的话
{
dist[j] = get_cost(t, j); //则选择建立电线!
fa[j] = t; //建立关系
}
}
return res;
}
int main()
{
cin >> n;
for (int i=1; i <= n; i ++)
scanf("%d%d", &q[i].x, &q[i].y); //输入第i座城市的坐标!
//输入每座城市的建立发电站的花费 和 连接电线的花费!
for (int i=1; i <= n; i ++)
scanf("%d", &wc[i]);
for (int i=1; i <= n; i ++)
scanf("%d", &wk[i]);
//将两两城市之间拉电线的操作看作是连接边,边权为:(abs(x2-x1)+abs(y2-y1))*(wk[2] + wk[1]);
printf ("%lld\n", prim());
printf ("%d\n", (int)ans1.size());
for (int i=0; i < ans1.size(); i ++)
printf ("%d ", ans1[i]);
puts("");
printf ("%d\n", (int)ans2.size());
for (auto& [x, y]:ans2)
{
printf ("%d %d\n", x, y);
}
return 0;
}