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;
}

posted @ 2021-12-22 17:28  Bellala  阅读(60)  评论(0)    收藏  举报