题意:有$n$个城市,第$i$个城市的坐标为$(x_i,y_i)$,每个城市都有一个$k_i$,现在你要在某些城市建发电站,第$i$个城市建发电站的花费为$c_i$,你可以在城市之间建电线,两个城市之间电线的花费为$(k_i+k_j)*(mid x_i-x_jmid + mid y_i-y_jmid )$,现在要让所有的城市都有电,输出最小花费,并且求出哪些城市建了电站,哪些城市之间建电线。
思路:建立一个超级源点,当某一个城市建发电站时,则视为该城市到超级源点有一条权值为$c_i$的无向边,所以把每个城市到超级源点之间建立一条权值为$c_i$的无向边,再将每两个城市之间电线的花费算出来,在两个城市之间建无向边,跑一遍$kruskal$即可,在跑的过程中记录一下哪些城市建了电站,哪些城市之间建了电线,在进行$kruskal$时,应该是$n+1$个点(一个为超级源点)。
#include <iostream> #include <algorithm> #include <cstdio> #include <vector> #include <cstring> using namespace std; typedef long long ll; const int N = 2010; const int M = N * N; struct node { int u, v; ll w; }; ll x[N], y[N], c[N], k[N]; int n, fa[N], tot, cnt1, cnt2; node edge[M]; int px[M], py[M], p[M]; void add_edge(int u, int v, ll w) { edge[tot].u = u; edge[tot].v = v; edge[tot++].w = w; } bool cmp(const node a, const node b) { return a.w < b.w; } int find(int x) { return -1 == fa[x] ? x : fa[x] = find(fa[x]); } ll Kruskal(int n) { memset(fa, -1, sizeof(fa)); sort(edge, edge + tot, cmp); int cnt = 0; ll ans = 0; for (int i = 0; i < tot; i++) { int u = edge[i].u, v = edge[i].v; ll w = edge[i].w; int t1 = find(u), t2 = find(v); if (t1 != t2) { if (0 == u || 0 == v) p[++cnt1] = u + v; else px[++cnt2] = u, py[cnt2] = v; ans += w, fa[t1] = t2, cnt++; } if (cnt == n - 1) break; } if (cnt < n - 1) return -1; else return ans; } int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%lld%lld", &x[i], &y[i]); for (int i = 1; i <= n; i++) scanf("%lld", &c[i]); for (int i = 1; i <= n; i++) scanf("%lld", &k[i]); for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { if (i != j) { ll w = (k[i] + k[j]) * (abs(x[i] - x[j]) + abs(y[i] - y[j])); add_edge(i, j, w); } } } for (int i = 1; i <= n; i++) { add_edge(0, i, c[i]); add_edge(i, 0, c[i]); } printf("%lld ", Kruskal(n + 1)); printf("%d ", cnt1); for (int i = 1; i <= cnt1; i++) { if (i == cnt1) printf("%d ", p[i]); else printf("%d ", p[i]); } printf("%d ", cnt2); for (int i = 1; i <= cnt2; i++) printf("%d %d ", px[i], py[i]); return 0; }