pku 1947 Rebuilding Roads 解题报告
算法: 一道不错的树形dp.不会做.看了大牛的状态转移方程才慢慢推敲出来.
dp[now][j]:记录now结点,要得到一棵j个节点的子树去掉的最少边数
1)考虑其儿子x
如果不去掉x子树,则dp[now][j] = min(dp[now][j-k]+dp[x][k]) 0 <= k <= j
2)如果去掉x子树,则dp[now][j] = dp[now][j] + 1
总的为:dp[now][j] = min (min(dp[now][j-k]+dp[x][k]), dp[now][j] + 1)
其实冷静想想,还是与一般的树形dp的解题思路差不多.想办法从顶搜索到树低,设置边界条件,再利用状态转移方程求解。
AC代码:
#include <iostream>
using namespace std;
#define M 155
#define inf 1000000
int N, P;
int Tree[M][M];
int dp[M][M]; //dp[i][j]以i为根有j个结点需要剪去的最少边数
void solve(int now)
{
int i, j, k, x;
//树形dp
for (i = 1; i <= Tree[now][0]; i++)
{
solve(Tree[now][i]);
}
//搜索到树低
dp[now][0] = 0;
for (i = 1; i <= Tree[now][0]; i++)
{
x = Tree[now][i];
//dp[now][j]:节点x为根的子树上保留j个节点的最优解
/*dp[now][j]:记录now结点,要得到一棵j个节点的子树去掉的最少边数
1)考虑其儿子x
如果不去掉x子树,则
dp[now][j] = min(dp[now][j-k]+dp[x][k]) 0 <= k <= j
2)如果去掉x子树,则
dp[now][j] = dp[now][j] + 1
总的为:
dp[now][j] = min (min(dp[now][j-k]+dp[x][k]), dp[now][j] + 1)
*/
for (j = N; j >= 0; j--)
{
//如果去掉x子树,则dp[now][j]=dp[now][j]+1;
if (dp[now][j] < inf)
{
dp[now][j]++;
}
//考虑到now的儿子x
//如果不去掉x子树,则
//dp[now][j] = min(dp[now][j-k]+dp[x][k]) 0 <= k <= j
for (k = 1; k <= j; k++)
{
if (dp[now][j] > dp[now][j - k] + dp[x][k])
{
dp[now][j] = dp[now][j - k] + dp[x][k];
}
}
}
}
for (i = N; i > 0; i--)
{
dp[now][i] = dp[now][i - 1];
}
}
int main()
{
//freopen("1.txt", "r", stdin);
int i, j, x, y;
while (scanf("%d%d", &N, &P) != EOF)
{
memset(Tree, 0, sizeof(Tree));
for (i = 1; i <= N; i++)
{
for (j = 1; j <= N; j++)
{
dp[i][j] = inf;
}
}
for (i = 1; i < N; i++)
{
scanf("%d%d", &x, &y);
Tree[x][++Tree[x][0]] = y;
}
solve(1);
int ans = dp[1][P];
for (i = 2; i <= N; i++)
{
//考虑其根结点+1
if (ans > dp[i][P] + 1)
{
ans = dp[i][P] + 1;
}
}
printf("%d/n", ans);
}
return 0;
}