Codeforces Round #438 by Sberbank and Barcelona Bootcamp (Div. 1 + Div. 2 combined) E - Policeman and a Tree

\(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 <bits/stdc++.h>

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<int> 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;
}
posted @ 2020-02-05 20:08  Mrzdtz220  阅读(120)  评论(0)    收藏  举报