题意:一棵n个结点的有根树,树的边有正整数权,表示两个结点之间的距离。你的任务是回答这样的询问:从根结点处罚,走不超过x单位距离,最多经过多少个结点?同一个结点经过最多算一次。
分析:设dp[x][j][0]表示以x为根的树经过j个点且回来的最小费用,dp[x][j][1]表示不会来的最小费用。
则有: dp[x][j][0] = min(dp[x][j][0],dp[x][j-k][0]+2*l+dp[u][k][0]);
dp[x][j][1] = min(dp[x][j][1],min(dp[x][j-k][0]+l+dp[u][k][1],dp[x][j-k][1]+2*l+dp[u][k][0]));
#include <vector>
#include <cstdio>
#include <utility>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAXN 547483647
using namespace std;
int n,q,x,y,v,num,root,cnt[505],tot[505],dp[505][505][2];
bool jud[505];
typedef pair<int,int> pii;
vector <pii> son[505];
void dfs(int x)
{
tot[x] = 1;
for(pii u : son[x])
{
dfs(u.first);
tot[x] += tot[u.first];
}
for(int j = 2;j <= tot[x];j++) dp[x][j][0] = dp[x][j][1] = MAXN;
dp[x][1][0] = dp[x][1][1] = dp[x][0][0] = dp[x][0][1] = 0;
for(int i = cnt[x];i;i--)
{
int u = son[x][i-1].first,l = son[x][i-1].second;
for(int j = tot[x];j;j--)
for(int k = 1;k <= tot[u] && k <= j;k++)
{
dp[x][j][0] = min(dp[x][j][0],dp[x][j-k][0]+2*l+dp[u][k][0]);
dp[x][j][1] = min(dp[x][j][1],min(dp[x][j-k][0]+l+dp[u][k][1],dp[x][j-k][1]+2*l+dp[u][k][0]));
}
}
}
int main()
{
cin.sync_with_stdio(false);
while(cin>>n && n)
{
cout<<"Case "<<++num<<":"<<endl;
for(int i = 1;i <= n;i++) son[i].clear();
memset(cnt,0,sizeof(cnt));
memset(tot,0,sizeof(tot));
memset(jud,0,sizeof(jud));
for(int i = 1;i < n;i++)
{
cin>>x>>y>>v;
x++,y++;
jud[x] = true;
son[y].push_back(make_pair(x,v));
cnt[y]++;
}
for(int i = 1;i <= n;i++)
if(!jud[i]) root = i;
dfs(root);
cin>>q;
for(int i = 1;i <= q;i++)
{
int x,j = tot[root];
cin>>x;
for(;j;j--) if(dp[root][j][1] <= x) break;
cout<<j<<endl;
}
}
}