pku 1947 Rebuilding Roads 树形dp 解题报告

来源:互联网 发布:古黛粉红知乎 编辑:程序博客网 时间:2024/05/20 13:06

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;

}