题目大意:一棵有n个节点的有根树,树的边有正整数权,表示两个节点之间的距离,你的任务是从根节点出发,走不超过x单位距离的,最多能经过多少个节点,每个节点只能计算一次
解题思路:用dis[u][v]表示u节点和v节点之间的距离,v节点是u节点的子节点
用dp[u][i][0]表示从u节点出发经过i个节点,最后没有回到节点u所走的最短路径
那么dp[u][i][0] = min(dp[u][i-k][1] + dp[v][k][0] + dis[u][v], dp[u][i-k][0] + dp[v][k][1] + 2 * dis[u][v])
dp[u][i][1]表示从u节点出发经过i个节点,最后回到了节点u所走的最短路径
则dp[u][i][1] = dp[u][i-k][1] + dp[v][k][1] + 2 * dis[u][v]
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#define maxn 510
using namespace std;
vector<int> tree[maxn];
int dp[maxn][maxn][2], n, dis[maxn][maxn], son[maxn], vis[maxn];
int dfs(int cur) {
son[cur] = 1;
int size = tree[cur].size();
for(int i = 0; i < size; i++)
son[cur] += dfs(tree[cur][i]);
dp[cur][1][0] = dp[cur][1][1] = 0;
for(int i = 0; i < size; i++) {
int next = tree[cur][i];
int len = dis[cur][next];
for(int j = son[cur]; j >= 1; j--)
for(int k = 1; k <= son[next] && k < j; k++) {
dp[cur][j][1] = min(dp[cur][j][1], dp[cur][j-k][1] + dp[next][k][1] + 2 * len);
dp[cur][j][0] = min(dp[cur][j][0], dp[cur][j-k][1] + dp[next][k][0] + len);
dp[cur][j][0] = min(dp[cur][j][0], dp[cur][j-k][0] + dp[next][k][1] + 2 * len);
}
}
return son[cur];
}
int main() {
int mark = 1;
while(scanf("%d", &n) == 1 && n) {
for(int i = 0; i < n; i++)
tree[i].clear();
memset(dp,0x3f,sizeof(dp));
memset(vis,0,sizeof(vis));
int x, y, d;
for(int i = 0; i < n - 1; i++) {
scanf("%d%d%d", &x, &y, &d);
tree[y].push_back(x);
dis[y][x] = d;
}
dfs(0);
int cnt, MAX;
printf("Case %d:\n",mark++);
scanf("%d", &cnt);
while(cnt--) {
scanf("%d", &MAX);
for(int i = n; i >= 0; i--)
if(dp[0][i][0] <= MAX){
printf("%d\n", i);
break;
}
}
}
return 0;
}