【LA4015】Caves【Tree DP】

https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2016

题意:

一棵n个点的树,从根节点出发,路程不超过s,最多可以遍历多少个点?


大白上的题。还是挺不错的tree dp。


设dp[u][x][0]表示u的子树中,从u出发,走过x个节点,不回到u的路程。

设dp[u][x][1]表示u的子树中,从u出发,走过x个节点,回到u的路程。
设v为u的儿子,len为(u, v)这条边的长度。

对于dp[u][x][0],有两种情况:

第一种,先去v的子树中走了i个节点,然后回到u,再去其他的子树中,不回到u,那么有dp[u][x][0] = dp[v][i][1] + len * 2 + dp[u][x- i][0]。

第二种,先去其他的子树中,回到u,然后去v的子树中走了i个节点,那么有dp[u][x][0] = dp[u][x - i][1] + len + dp[v][i][0]。

对于dp[u][x][1],只有一种情况:

走过其他子树和v的子树,在v的子树中走了i个节点,然后回到u,那么有dp[u][x][1] = dp[v][i][1] + dp[u][x - i][1] + len * 2。


方程写出来类似于01背包,于是x要从大到小枚举。


没注意输出里还有个Case,wa了发...


懵逼,虽然题里没说0是根节点,但是数据里的确都是0为根节点。于是就可以少开一个度数数组啦。


/* Footprints In The Blood Soaked Snow */
#include <cstdio>
#include <algorithm>

using namespace std;

const int maxn = 505, maxm = maxn, inf = 0x3f3f3f3f;

int n, head[maxn], cnt, size[maxn], dp[maxn][maxn][2];

struct _edge {
	int v, w, next;
} g[maxm << 1];

inline int iread() {
	int f = 1, x = 0; char ch = getchar();
	for(; ch < '0' || ch > '9'; ch = getchar()) f = ch == '-' ? -1 : 1;
	for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
	return f * x;
}

inline void add(int u, int v, int w) {
	g[cnt] = (_edge){v, w, head[u]};
	head[u] = cnt++;
}

inline void dfs(int u) {
	size[u] = 1;
	dp[u][1][0] = dp[u][1][1] = 0;
	for(int i = head[u]; ~i; i = g[i].next) {
		int v = g[i].v;
		dfs(v);
		size[u] += size[v];
		for(int x = size[u]; x > 0; x--) for(int j = 1; j <= size[v]; j++) {
			if(x - j < 1) break;
			dp[u][x][0] = min(dp[u][x][0], min(dp[v][j][1] + dp[u][x - j][0] + g[i].w * 2, dp[v][j][0] + dp[u][x - j][1] + g[i].w));
			dp[u][x][1] = min(dp[u][x][1], dp[v][j][1] + dp[u][x - j][1] + g[i].w * 2);
		}
	}
}

int main() {
	for(int cas = 1; ; cas++) {
		n = iread();
		if(!n) break;

		for(int i = 0; i <= n; i++) head[i] = -1; cnt = 0;
		for(int i = 0; i <= n; i++) for(int j = 0; j <= n; j++) dp[i][j][0] = dp[i][j][1] = inf;

		for(int i = 1; i < n; i++) {
			int u = iread(), v = iread(), w = iread(); u++; v++;
			add(v, u, w);
		}

		dfs(1);

		printf("Case %d:\n", cas);
		int T = iread();
		while(T--) {
			int s = iread(), ans = 0;
			for(int i = 0; i <= n; i++) if(dp[1][i][0] <= s || dp[1][i][1] <= s) ans = i;
			printf("%d\n", ans);
		}
	}
}


你可能感兴趣的:(dp,tree)