题意:给定n个节点组成的树,树有边权.现在要在一些点上建立消防站,每个点建站都有个cos[i],如果不在当前的点上建站,也要依赖其他的消防站,并且距离不超过lim[i]。求符合上述条件的最小费用建站方案。n <= 1000.
思路:树形DP,dp[i][j]表示以i为根的子树满足条件,并且i依赖于j节点的消防站所需的最小代价。base[i]为以i为根的子树满足条件所需要的最小代价。转移方程为:dp[i][j] = cos[j]+∑min(dp[k][j]-cos[j], base[k]), base[i] = min(dp[i][j])。因为i依赖于j,所以子树中如果有依赖于j的状态则需要减去一次cos[j],否则会多算代价。
复杂度为O(n^2)。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define N 1003
struct E{
int v, d, ne;
E(){}
E(int _v, int _d, int _ne):v(_v), d(_d), ne(_ne){}
}e[N*N*2];
int n, size, head[N], lim[N], cos[N], dis[N][N];
int dp[N][N], base[N];
void init() {
size = 0;
memset(head, -1, sizeof(head));
memset(dp, 0x3f, sizeof(dp));
memset(base, 0x3f, sizeof(base));
}
void add(int u, int v, int d) {
e[size] = E(v, d, head[u]);
head[u] = size++;
}
void cal(int u, int fa, int top, int d) {
dis[top][u] = d;
int i, v;
for (i = head[u];~i;i = e[i].ne) {
v = e[i].v;
if (v == fa) continue;
cal(v, u, top, d+e[i].d);
}
}
void dfs(int u, int f) {
int i, v, j;
vector<int>V;
for (i = head[u];~i;i = e[i].ne) {
v = e[i].v;
if (v == f) continue;
dfs(v, u);
V.push_back(v);
}
for (i = 1;i <= n;i++) {
if (dis[u][i] > lim[u]) continue;
dp[u][i] = cos[i];
for (j = 0;j < V.size();j++) {
dp[u][i] += min(dp[V[j]][i]-cos[i], base[V[j]]);
}
base[u] = min(base[u], dp[u][i]);
}
}
int main() {
int T, i, j, u, v, d;
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
init();
for (i = 1;i <= n;i++) {
scanf("%d", &cos[i]);
}
for (i = 1;i <= n;i++) {
scanf("%d", &lim[i]);
}
for (i = 1;i < n;i++) {
scanf("%d%d%d", &u, &v, &d);
add(u, v, d), add(v, u, d);
}
for (i = 1;i <= n;i++) {
cal(i, -1, i, 0);
}
dfs(1, -1);
printf("%d\n", base[1]);
}
}