poj1947(树形dp)

 

题目链接:http://poj.org/problem?id=1947

题意:给n(n<=150)个点的一棵树,求删掉最少边数k使得最后该树只剩下p(1<=p<=n)个节点。(求最小的k)

分析:设dp[u][j]表示以u节点为根的子树保留j个节点删掉最少的边数;则dp[u][j]=min(dp[u][j],dp[u][j-k]+dp[v][k]).初始值dp[u][1]=0.

#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
#include <cstdlib>
#include <stack>
#include <vector>
#include <set>
#include <map>
#define LL long long
#define mod 1000000007
#define inf 0x3f3f3f3f
#define N 250
#define clr(a) (memset(a,0,sizeof(a)))
using namespace std;
struct edge
{
    int next,v;
    edge(){}
    edge(int v,int next):v(v),next(next){}
}e[N*2];
int head[N],tot,n,m;
int dp[N][N];
void addedge(int u,int v)
{
    e[tot]=edge(v,head[u]);
    head[u]=tot++;
}
void dfs(int u,int fa)
{
    dp[u][1]=0;
    for(int i=head[u];~i;i=e[i].next)
    {
        int v=e[i].v;
        if(v==fa)continue;
        dfs(v,u);
        for(int j=m;j>=1;j--)
        {
            dp[u][j]++;//对于子树u,要保持j个节点不变,必须砍掉该条边去掉子树v
            for(int k=1;k<j;k++)
                dp[u][j]=min(dp[u][j],dp[u][j-k]+dp[v][k]);
        }
    }
}
int main()
{
    int u,v;
    while(scanf("%d%d",&n,&m)>0)
    {
        tot=0;
        memset(head,-1,sizeof(head));
        memset(dp,0x3f,sizeof(dp));
        for(int i=1;i<n;i++)
        {
            scanf("%d%d",&u,&v);
            addedge(u,v);
            addedge(v,u);
        }
       dfs(1,-1);
       int ans=dp[1][m];
       for(int i=2;i<=n;i++)ans=min(ans,dp[i][m]+1);
       printf("%d\n",ans);
    }
}
View Code

 

posted on 2015-01-05 19:08  lienus  阅读(165)  评论(0编辑  收藏  举报

导航