码迷,mamicode.com
首页 > 其他好文 > 详细

[CF868E]Policeman and a Tree

时间:2019-07-16 11:06:11      阅读:34      评论:0      收藏:0      [点我收藏+]

标签:返回   out   false   ++i   ant   lse   iostream   min   ret   

题目大意:有一棵$n$个点的带边权的树,上面有$m$个罪犯,速度为任意大,有一个警察在点$S$,速度为$1$。若警察和罪犯在同一个地方,罪犯就被干掉了,警察希望干掉所有罪犯时间最短,而罪犯希望最大化这个时间。求出这个时间,若无解输出 Terrorists win 。$n,m\leqslant 50$

题解:一定有解,令$f[u][v][x][y]$表示从警察$u$走到$v$,$v$关于$u$的子树内有$x$个罪犯,另外的节点还有$y$个罪犯的最小时间。警察一定选择$dp$值最小的走,而罪犯的分布会使每条边$dp$值得最小值最大。用类似背包的东西转移。在转移时另开一个数组$g[i][j]$表示现在是$v$的第$i$棵子树,放了$j$个罪犯所需的时间。
$$
g[i][j]=\max\{g[i][j],\min\{g[i-1][j-k],dp[v][v‘][k][x+y-k]+w\}\}
$$
$v‘$为$v$的第$i$棵子树,$k$为$v‘$这棵子树放几个罪犯,$w$为$v\to v‘$的边权。$g$明显可以用$01$背包的方法把第一维滚掉。到叶子的时候抓住罪犯然后返回。记忆化搜索即可。

卡点:

 

C++ Code:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
const int maxn = 60, inf = 0x3f3f3f3f;

int head[maxn], cnt = 1, deg[maxn];
struct Edge {
	int to, nxt, w;
} e[maxn << 1];
inline void addedge(int a, int b, int c) {
	e[++cnt] = (Edge) { b, head[a], c }; head[a] = cnt;
	e[++cnt] = (Edge) { a, head[b], c }; head[b] = cnt;
	++deg[a], ++deg[b];
}

int n, m, S, sz[maxn];
int f[maxn << 1][maxn][maxn];
void dfs(int u, int fa = 0) {
	for (int i = head[u], v; i; i = e[i].nxt) {
		v = e[i].to;
		if (v != fa) dfs(v, u), sz[u] += sz[v];
	}
}
int dp(int E, int x, int y) {
	if (!x && !y) return 0;
	int &F = f[E][x][y], u = e[E].to;
	if (~F) return F;
	if (deg[u] == 1) {
		if (y == 0) return 0;
		return F = dp(E ^ 1, y, 0) + e[E].w;
	}
	int g[maxn];
	memset(g, 0, sizeof g), g[0] = inf;
	for (int i = head[u], v; i; i = e[i].nxt) if (i ^ E ^ 1) {
		v = e[i].to;
		for (int j = x; j; --j)
			for (int k = j; k; --k)
				g[j] = std::max(g[j], std::min(g[j - k], dp(i, k, x + y - k) + e[i].w));
	}
	return F = g[x];
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	std::cin >> n;
	for (int i = 1, a, b, c; i < n; ++i) {
		std::cin >> a >> b >> c;
		addedge(a, b, c);
	}
	std::cin >> S >> m;
	for (int i = 0, x; i < m; ++i) std::cin >> x, ++sz[x];
	dfs(S), memset(f, -1, sizeof f);
	int ans = inf;
	for (int i = head[S], v; i; i = e[i].nxt) {
		v = e[i].to;
		ans = std::min(ans, dp(i, sz[v], m - sz[v]) + e[i].w);
	}
	std::cout << ans << ‘\n‘;
	return 0;
}

  

[CF868E]Policeman and a Tree

标签:返回   out   false   ++i   ant   lse   iostream   min   ret   

原文地址:https://www.cnblogs.com/Memory-of-winter/p/11193001.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有 京ICP备13008772号-2
迷上了代码!