[CF868E]Policeman and a Tree

Description

有一棵树, 边有边权.

树上有 \(k\) 个小偷移动速度为正无穷,警察初始在 \(s\) 位置,移动速度为 \(1\) , 求至少多少时间才能将抓住所有小偷(小偷和警察都进行最优决策,小偷尽可能拖延时间).

\(n\le 50\)

Solution

看到这题数据范围非常小,令我们去想一个高维dp。

\(dp[p][u][s_1][s_2]\) 表现警察准备从\(p\)走到\(u\),正对着的子树有\(s_1\)个小偷,其他还有\(s_2\)个小偷,并且抓住所有小偷的最小时间。

那么就可以直接枚举在一颗子树中留有多少小偷,并往那颗子树中走,做树上背包转移了,注意边界:

  • \(s_1\)\(0\)\(s_2\)\(0\),那么任务完成,要0的时间。
  • \(s_1\)\(0\)\(s_2\)不为\(0\),那么这一步是没有意义的,赋值为 \(INF\)
  • u是叶子节点,有s1个点,那么 \(dp[p][u][s_1][s_2]\leftarrow dp[u][p][s_2][0] + edgeval(u, v)\),抓到 \(s_1\) 个人后返回。

Code

#include <iostream>
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <fstream>

typedef long long LL;
typedef unsigned long long uLL;

#define SZ(x) ((int)x.size())
#define ALL(x) (x).begin(), (x).end()
#define MP(x, y) std::make_pair(x, y)
#define DE(x) cerr << x << endl;
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define GO cerr << "GO" << endl;
#define rep(i, a, b) for (register int (i) = (a); (i) <= (b); ++(i))

using namespace std;

inline void proc_status()
{
	ifstream t("/proc/self/status");
	cerr << string(istreambuf_iterator<char>(t), istreambuf_iterator<char>()) << endl;
}
inline int read() 
{
	register int x = 0; register int f = 1; register char c;
	while (!isdigit(c = getchar())) if (c == '-') f = -1;
	while (x = (x << 1) + (x << 3) + (c xor 48), isdigit(c = getchar()));
	return x * f;
}
template<class T> inline void write(T x) 
{
	static char stk[30]; static int top = 0;
	if (x < 0) { x = -x, putchar('-'); }
	while (stk[++top] = x % 10 xor 48, x /= 10, x);
	while (putchar(stk[top--]), top);
}
template<typename T> inline bool chkmin(T &a, T b) { return a > b ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return a < b ? a = b, 1 : 0; }

const int maxN = 50;

int n, m, police;
vector<int> g[maxN + 1];
int edge[maxN + 1][maxN + 1], size[maxN + 1], deg[maxN + 2];
int dp[maxN + 1][maxN + 1][maxN + 1][maxN + 1];

void link(int u, int v, int w)
{
	deg[u]++;
	g[u].push_back(v);
	edge[u][v] = w;
}

void Input()
{
	n = read();
	for (int i = 1; i < n; ++i)
	{
		int u = read(), v = read(), w = read();
		link(u, v, w), link(v, u, w); 
	}
	police = read();
	m = read();
	for (int i = 1; i <= m; ++i) size[read()]++;
}

void dfs(int u, int fa)
{
	for (int v : g[u])
		if (v != fa)
			dfs(v, u), size[u] += size[v];
}

int DP(int p, int u, int s1, int s2)
{
	if (!s1 and !s2) return 0;
	if (!s1) return 0x3f3f3f3f;
	if (dp[p][u][s1][s2] != -1) return dp[p][u][s1][s2];
	if (deg[u] == 1) return dp[p][u][s1][s2] = DP(u, p, s2, 0) + edge[p][u];
	int tmp[maxN + 2];
	memset(tmp, 0, sizeof tmp);
	tmp[0] = 0x3f3f3f3f;
	for (int v : g[u])
		if (v != p)
		{
			for (int i = s1; i >= 0; --i)
				for (int j = 0; j <= i; ++j)
					chkmax(tmp[i], min(tmp[i - j], DP(u, v, j, s1 + s2 - j)));
		}
	return dp[p][u][s1][s2] = tmp[s1] + edge[p][u];
}

void Solve()
{
	memset(dp, -1, sizeof dp);
	dfs(police, 0);
	int ans = 0x3f3f3f3f;
	for (int v : g[police])
		chkmin(ans, DP(police, v, size[v], m - size[v]));
	printf("%d\n", ans);
}

int main() 
{
#ifndef ONLINE_JUDGE
	freopen("CF868E.in", "r", stdin);
	freopen("CF868E.out", "w", stdout);
#endif

	Input();

	Solve();

	return 0;
}
posted @ 2019-09-02 20:17  茶Tea  阅读(250)  评论(0编辑  收藏  举报