简单的树形dp

题意: 给出一个树, 现在想得到一个有p个节点的子树,至少需要删除几条边。

思路: 给出一个树并且还要求一个最小值很容易想到要用树形dp。

我开的dp方程 dp[i][j]表示以i为父节点有j个节点的子树需要删掉几条边。

这个方程我是这样推的。 当面对一个子树的时候, 如果不要这个子树的东西,那么需要消耗1, 如果要了的话那么就是一个背包dp。 这样的话就在中间加一个中间变量数组。详见代码;

View Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
using namespace std;
const int N = 155, INF = 1<<29;

struct EDGE
 {
     int u, v, next;
 }edge[N*2];

int num, head[N], dp[N][N], n, p;

void add(int u, int v)
 {
     edge[num].u = u;
     edge[num].v = v;
     edge[num].next = head[u];
     head[u] = num++;
 }

void init()
 {
     num = 0;
     memset(head, -1, sizeof(head));
     int u, v;
     for(int i=1; i<n; i++)
      {
          scanf("%d%d", &u, &v);
          add(u, v);
          add(v, u);
      }
 }

void dfs(int u, int pre)
 {
     dp[u][1] = 0;
     int v;
     int g[N];
     for(int i=head[u]; i!=-1; i=edge[i].next)
      {
          v = edge[i].v;
          if(v == pre) continue;
          for(int j=1; j<=p; j++)
           g[j] = n;
          dfs(v, u);
          for(int j=p; j>=1; j--)
           {
               for(int k=1; k<=p; k++)
                {
                    if(j+k > p) break;
                    g[j+k] = min(g[j+k], dp[u][j]+dp[v][k]);
                }
           }
           for(int j=1; j<=p; j++)
            dp[u][j] = min(dp[u][j]+1,g[j]);
      }
 }

void solve()
 {
     for(int i=1; i<=n; i++)
      {
          for(int j=0; j<=p; j++)
           dp[i][j] = n;
      }
    dfs(1,-1);
    int ans = dp[1][p];
    for(int i=2; i<=n; i++)
     ans = min(ans, dp[i][p]+1);
    printf("%d\n",ans);
 }

int main()
 {
     while(scanf("%d%d", &n, &p) != EOF)
      {
          init();
          solve();
      }
     return 0;
 }

 

 

posted @ 2012-09-24 22:49  Gu Feiyang  阅读(158)  评论(0)    收藏  举报