cf1245 D. Shichikuji and Power Grid(最小生成树)
题意:
每个点用坐标表示,在第 \(i\) 个点建电站的花费为 \(c_i\),在两点 \(i,j\) 之间拉电线的代价为 \((k_i+k_j)d\) ,\(d\) 为曼哈顿距离。要求每个点要么有电站,要么与一个有点站的点连通,求最小花费并输出一种方案。
思路:
建立超级源点0,0号点到每个点都连一条边,权值为每个点建电站的花费。然后跑一遍prim,思路和Dijkstra一样:维护last数组记录每个点最后被哪个点更新,在v[x]=1时更新答案。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 2005;
int n, x[N], y[N], c[N], k[N];
ll dist(int i, int j) //计算边权
{
//if(i == j) return 0;
if(i == 0) return c[j];
if(j == 0) return c[i];
return (ll)(k[i] + k[j]) * (abs(x[i]-x[j]) + abs(y[i]-y[j]));
}
ll d[N]; bool v[N]; int last[N];
vector<int> dian; vector<pair<int, int>> bian; ll ans;
void prim()
{
memset(d, 0x3f, sizeof d), memset(v, 0, sizeof v); d[0] = 0;
for(int i = 0; i <= n; i++)
{
int x = -1;
for(int j = 0; j <= n; j++)
if(!v[j] && (x == -1 || d[j] < d[x])) x = j;
v[x] = 1;
if(x > 0)
{
ans += d[x];
if(last[x] > 0) bian.push_back({x, last[x]}); //拉电线
else dian.push_back(x); //建电站
}
for(int y = 0; y <= n; y++) if(!v[y])
if(d[y] > dist(x, y)) d[y] = dist(x, y), last[y] = x;
}
}
signed main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d%d", &x[i], &y[i]);
for(int i = 1; i <= n; i++) scanf("%d", &c[i]);
for(int i = 1; i <= n; i++) scanf("%d", &k[i]);
prim();
printf("%lld\n%d\n", ans, dian.size());
for(int i : dian) printf("%d ", i);
printf("\n%d\n", bian.size());
for(auto i : bian) printf("%d %d\n", i.first, i.second);
return 0;
}

浙公网安备 33010602011771号