题意:一棵n(n<=500)个节点的有根树,树的边有正整数权,表示两个节点之间的距离,你的任务是回答这样的询问,从根节点出发,走不超过x(x<=5000000)单位的距离,最多能走多少个节点,节点经过多次算一个,对于每次的询问(群文次数小于1000)输出:经过节点数最大的值。 注意题目给出的 i, j,d,其中 j 是 i 的父节点。
这道题想了两天,看了题解一开始也不明白(泪目),后来一想可以当成树上的01背包来做,终于ac,这应该是目前做的最难的一道动态规划题了。
思路是:这道题的难点之一是可以返回父节点,而且由于x太大,状态里假如有x那么数组太大开不下,我们要另外选择一种方式表示状态,对此我们可以用两个状态来表示,用d[i][x][j+k][0]表示在以x结点为根结点的树中,从前i颗子树中访问j+k个结点且返回到x结点(返回用零表示)所需要的最小花费;d[i][x][j+k][1]类似,只是不用返回到x结点(用1表示),这其实就有点类似于零一背包了,但这样也有问题,数组还是太大,开不下,这时就要让数组滚动起来,用son数组记录x结点的孩子节点总数,枚举son时从大到小,这样可以把状态的第一位省掉,那么我们就可以得到状态方程了
d[x][j+k][0] = min(d[x][j + k][0], d[x][j][0] + d[t.to][k][0] + t.val * 2);//t表示x的孩子节点,val代表花费
d[x][j+k][1] = min(d[x][j + k][1], min(d[x][j][0] + d[t.to][k][1] + t.val, d[x][j][1] + d[t.to][k][0] + 2 * t.val));
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<map>
#include<set>
using namespace std;
#define LL long long
const int maxn = 500 + 10;
const int maxx = 5000000 + 100;
const int INF = 0x3f3f3f3f;
int d[maxn][maxn][2], pa[maxn], son[maxn];
struct Edge {
int from, to, val;
};
vector<int> G[maxn];
vector<Edge> edges;
void dfs(int x) {
d[x][1][0] = d[x][1][1] = 0;
son[x] = 1;
for(int i = 0; i < G[x].size(); i++) {
Edge t = edges[G[x][i]];
dfs(t.to);
for(int j = son[x]; j >= 1; j--) {
for(int k = 1; k <= son[t.to]; k++) {
d[x][j+k][0] = min(d[x][j + k][0], d[x][j][0] + d[t.to][k][0] + t.val * 2);
d[x][j+k][1] = min(d[x][j + k][1], min(d[x][j][0] + d[t.to][k][1] + t.val, d[x][j][1] + d[t.to][k][0] + 2 * t.val));
}
}
son[x] += son[t.to];
}
}
int main() {
//freopen("input.txt", "r", stdin);
int n, kase = 0;
while(scanf("%d", &n) == 1 && n) {
edges.clear();
for(int i = 0; i < n; i++) G[i].clear();
memset(pa, -1, sizeof(pa));
memset(d, INF, sizeof(d));
for(int k = 0; k < n - 1; k++) {
int i, j, val; scanf("%d%d%d", &i, &j, &val);
edges.push_back((Edge){ j, i, val});
G[j].push_back(edges.size() - 1);
pa[i] = j;
}
int root = 0;
while(pa[root] != -1) root = pa[root];
dfs(root);
int Q; scanf("%d", &Q);
printf("Case %d:\n", ++kase);
while(Q--) {
int x; scanf("%d", &x);
int ans = 1;
for(int i = 1; i <= n; i++) {
if(x >= d[root][i][0]) ans = i;
else if(x >= d[root][i][1]) ans = i;
else break;
}
printf("%d\n", ans);
}
// for(int i = 1; i <= n; i++) printf("%d %d\n", d[root][i][0], d[root][i][1]);
}
return 0;
}uvalive 4015 洞穴cave(树的dp/01背包)
原文地址:http://blog.csdn.net/u014664226/article/details/46137351