\(dp[p][u][s_0][tol]\) 表示警察从 \(p\) 走到 \(u\),\(u\) 相对于 \(p\) 的子树内有 \(s_0\) 个犯人,全局总共有 \(tol\) 个犯人的最小花费时间。
当 \(tol = 0\) 时,花费为 \(0\)。
当 \(s_0=0\) 时,花费为 \(\text{INF}\),因为这一步是没有意义的。
当 \(u\) 为叶子时,\(dp[p][u][s_0][tol]=dp[u][p][tol-s_0][tol-s_0]+cost(p,u)\),走进这个叶子抓完犯人再往回走即可。
否则就类似于树形背包的转移
\(f[i][j]\) 表示解决完前 \(i\) 棵子树里共 \(j\) 个犯人的花费。
\(f[i][j+k] = \max\{f[i][j+k],\min\{f[i-1][j],dp[u][v][k][tol]\}\}\)
警察会选择DP值最小的走,而犯人会选择最大的方式来分布它们的位置,这是外层取 \(\max\) 内层取 \(\min\) 的原因,记忆化搜索。
因为 \(f\) 是共用的,那么转移前先调用一遍再转移。
#include
const int N = 55;
const int INF = 0x3f3f3f3f;
int dp[N][N][N][N], f[N][N];
int n, root, sz[N], deg[N], val[N][N];
std::vector adj[N];
void dfs(int u, int f) {
for (int i = 0; i < adj[u].size(); i++) {
int v = adj[u][i];
if (v == f) continue;
dfs(v, u);
sz[u] += sz[v];
}
}
int DP(int p, int u, int s0, int tol) {
if (!tol) return dp[p][u][s0][tol] = 0;
if (!s0) return dp[p][u][s0][tol] = INF;
if (dp[p][u][s0][tol] < INF) return dp[p][u][s0][tol];
if (deg[u] == 1) return dp[p][u][s0][tol] = DP(u, p, tol - s0, tol - s0) + val[p][u];
for (int i = 0; i < adj[u].size(); i++) {
int v = adj[u][i];
if (v == p) continue;
for (int j = 0; j <= s0; j++)
DP(u, v, j, tol);
}
for (int i = 0; i < 2; i++)
for (int j = 0; j <= s0; j++)
f[i][j] = 0;
f[0][0] = INF;
int cur = 1, last = 0;
for (int i = 0; i < adj[u].size(); i++) {
int v = adj[u][i];
if (v == p) continue;
memset(f[cur], 0, sizeof(f[cur]));
for (int j = 0; j <= s0; j++)
for (int k = 0; j + k <= s0; k++)
f[cur][j + k] = std::max(f[cur][j + k], std::min(f[last][j], dp[u][v][k][tol]));
std::swap(cur, last);
}
return dp[p][u][s0][tol] = f[last][s0] + val[p][u];
}
int main() {
scanf("%d", &n);
for (int i = 1, u, v, w; i < n; i++) {
scanf("%d%d%d", &u, &v, &w);
adj[u].push_back(v); adj[v].push_back(u);
val[u][v] = val[v][u] = w;
deg[u]++; deg[v]++;
}
scanf("%d", &root);
int m;
scanf("%d", &m);
for (int i = 1, u; i <= m; i++) {
scanf("%d", &u);
sz[u]++;
}
dfs(root, 0);
memset(dp, 0x3f, sizeof(dp));
int ans = INF;
for (int i = 0; i < adj[root].size(); i++) {
int v = adj[root][i];
ans = std::min(ans, DP(root, v, sz[v], m));
}
printf("%d\n", ans);
return 0;
}