树形dp,自己想了很久很久都没想起来怎么做。。。汗,真笨啊。只好看了一下别人的思路,挺巧妙的,原来树形dp的第三维这么能这么表示,看来以后要让自己的思维更加发散些才能做的出来这种题了。dp[u][l][f] ,u表示当前节点,l表示该节点能到达l个节点所走路程的最小值。当f = 1时表示不用回到当前点,f = 0表示必须回到当前点。则状态转移方程可以表示为:
dp[u][j][0] = min(dp[u][j][0], dp[v][k][0] + dp[u][j - k][0] + 2 * E[i].d);
dp[u][j][1] = min(dp[u][j][1], min(dp[v][k][0] + dp[u][j - k][1] + 2 * E[i].d, dp[v][k][1] + dp[u][j - k][0] + E[i].d));
知道了状态转移方程就好写多了~~~
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define CLR(a, b) memset(a, b, sizeof(a))
using namespace std;
const int N = 555;
const int INF = 1e9;
struct Edge
{
int u, v, d;
}E[N * 2];
int fir[N], next[N * 2], tot;
int dp[N][N][2];
int ans[N], n;
int sum[N];
void Add_edge(int u, int v, int d)
{
E[tot].u = u, E[tot].v = v, E[tot].d = d;
next[tot] = fir[u], fir[u] = tot ++;
}
int B_ser(int l, int r, int key)
{
int m;
while(l <= r)
{
m = (l + r) >> 1;
if(ans[m] == key) return m;
else if(ans[m] < key) l = m + 1;
else r = m - 1;
}
return r;
}
void dfs(int u, int f)
{
int v, i, j, k;
for(i = fir[u]; ~i; i = next[i])
{
v = E[i].v;
if(v != f)
{
dfs(v, u);
sum[u] += sum[v];
for(j = sum[u]; j > 1; j --)
{
for(k = min(j - 1, sum[v]); k > 0; k --)
{
dp[u][j][0] = min(dp[u][j][0], dp[v][k][0] + dp[u][j - k][0] + 2 * E[i].d);
dp[u][j][1] = min(dp[u][j][1], min(dp[v][k][0] + dp[u][j - k][1] + 2 * E[i].d, dp[v][k][1] + dp[u][j - k][0] + E[i].d));
}
}
}
}
}
int main()
{
//freopen("input.txt", "r", stdin);
int u, v, i, d, q, l, j, cas = 1;
while(scanf("%d", &n), n)
{
printf("Case %d:\n", cas ++);
tot = 0;
CLR(fir, -1);CLR(dp, 0);
sum[0] = 1;
for(i = 1; i < n; i ++)
{
sum[i] = 1;
scanf("%d%d%d", &u, &v, &d);
Add_edge(u, v, d);
Add_edge(v, u, d);
}
for(i = 0; i < n; i ++)
{
for(j = 2; j <= n; j ++)
{
dp[i][j][0] = dp[i][j][1] = INF;
}
dp[i][1][0] = dp[i][1][1] = 0;
}
dfs(0, -1);
for(i = 1; i <= n; i ++)
{
ans[i] = min(dp[0][i][0], dp[0][i][1]);
}
scanf("%d", &q);
while(q --)
{
scanf("%d", &l);
printf("%d\n", B_ser(1, n, l));
}
}
}