題目鏈接
基本算法
最小樹形圖基於貪心和縮點的思想。
1. 求最短弧集合E0
從所有以vi 爲終點的弧中取一條最短的,若對於vi ,沒有入邊,則不存在最小樹形圖,算法結束;如果能取,則得到有n個點和n-1條邊組成的圖G 的一個子圖G′ ,該子圖的權值一定是的最小的,但是不一定是一棵樹。
2. 檢查E0
若E0 沒有有向環且不含收縮點,則計算結束,E0 就是以G 的v0 爲根的最小樹形圖。若E0 沒有有向環,但含收縮點,則轉步驟(4),若E0 含有有向環C,則轉入步驟(3)。
3. 收縮G 的C收縮成點u,對於圖G 中兩端都屬於C的邊的都被收縮掉了,其它弧仍保留,得到一個新的圖G1 ,G1 中以收縮點爲終點的弧的長度都要變化,變化的規律是:設點v 在環C中,且環中指向v 的邊長爲w,點v′ 不在環C中,則對於每一條邊<v,v′> ,在G1 中有邊<v′,u> 與其對應,且權值W(G1)(<v′,u>)=WG(<v′,v>)−w 。對於圖G 中以環C爲C的起點的邊<v′,v> , 在圖中G1 有邊<u,v′> ,則W(G1)(<u,v′>)=WG(<v,v′>) 。
4. 展開縮點。
題意
每個家庭挖水井需要花費z*X, 如果挖水渠就是地勢高的家庭到地勢低的曼哈頓距離乘以Y,若是低到高在加Z。最後求最小花費。
解析
建圖的最難處理的就是那個,自己挖水井。建立一個源點,和每個點建邊,權值爲在z*X,剩下的就根據關係建邊即可。最後求有向圖的最小樹形圖即可。
代碼
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn = 1000+100;
int dfn[maxn], low[maxn];
int st[maxn];
int stk = 0;
struct node {
int u, v, w;
node ()
{}
node (int _u, int _v, int _w) {
u = _u;
v = _v;
w = _w;
}
}e[maxn*maxn];
int dep = 0, bcc_cnt;
int in[maxn], vis[maxn], id[maxn], pre[maxn];
vector<int>g[maxn];
struct point {
int x, y, z;
point(){}
}p[maxn];
int dis(point a, point b) {
return abs(a.x-b.x)+abs(a.y-b.y)+abs(a.z-b.z);
}
int Directed_MST(int root, int n, int m) {
int ret = 0;
while (1) {
//第一步:找到入邊最小邊
for (int i=0; i<n; i++)
in[i] = INF;
for (int i=0; i<m; i++) {
int u = e[i].u, v = e[i].v;
if (e[i].w < in[v] && u != v) {
in[v] = e[i].w;
pre[v] = u;
}
}
for (int i=0; i<n; i++) { //沒有入邊,就不會產生最小樹形圖
if (i == root)
continue;
if (in[i] == INF)
return -1;
}
int cntnode = 0;
memset(id, -1, sizeof(id));
memset(vis, -1, sizeof(vis));
// 第二步:找環
in[root] = 0;
for (int i=0; i<n; i++) {
ret += in[i];
int v = i;
while (vis[v] != i && id[v] == -1 && v != root) {
vis[v] = i;
v = pre[v];
}
if (v != root && id[v] == -1) {
for (int u=pre[v]; u!=v; u=pre[u])
id[u] = cntnode;
id[v] = cntnode++;
}
}
if (cntnode == 0)
break;
for (int i=0; i<n; i++) {
if (id[i] == -1)
id[i] = cntnode++;
}
//第三步:縮點、重新標記
for (int i=0; i<m; i++) {
int v = e[i].v;
e[i].u = id[e[i].u];
e[i].v = id[e[i].v];
if (e[i].u != e[i].v)
e[i].w -= in[v];
}
n = cntnode;
root = id[root];
}
return ret;
}
int main() {
int n, X, Y, Z;
while (~scanf("%d%d%d%d", &n, &X, &Y, &Z) && n) {
int S = 0;
int m = 0;
for (int i=1; i<=n; i++) {
scanf("%d%d%d", &p[i].x, &p[i].y, &p[i].z);
e[m++] = node(S, i, p[i].z*X);
g[S].push_back(i);
}
for (int i=1; i<=n; i++) {
int k;
scanf("%d", &k);
int u = i;
while (k--) {
int v;
scanf("%d", &v);
if (v == i)
continue;
int w = dis(p[u], p[v])*Y;
if (p[u].z < p[v].z)
w += Z;
e[m++] = node (u, v, w);
}
}
int ans = Directed_MST(0, n+1, m);
printf("%d\n", ans);
}
return 0;
}