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); } } }