1. 程式人生 > 其它 >cf1245 D. Shichikuji and Power Grid(最小生成樹)

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