这个题和前一篇很像啊,导致我昨天晚上冥思苦想结果误入歧途还自以为是。先说说哪里像吧:前一个题是说彼此之间有所属关系,贿赂一个人,整个子树都会给她投票,问至少需要m人时至少需要多少钱;这个题说对于一个树,减去几条边能剩一个有p个节点的子树,首先,子树,不是严格意义上的子树,是如图这种:

所以这就导致我误认为,把删去的子树节点数求和,每删去一个子树,删边的个数+1,一个01背包就搞定了,最终求dp[n-p]即可,但是!且不说他给的数据有可能形成形成森林的情况,也有可能需要在一颗子树中减去几条分支只要一个子树中的一部分啊,之前那个题的做法就不适用了耶,怎么办呢?dp[i][j]代表以i为根的子树要变成j个节点需要剪掉的边数。对于这个子树i从他的父节点分离出来这个剪枝步数不用考虑,最后统一加一就好。当然了,自己调程序的时候这里也卡了一下,这个点要是根节点的话就不加一了啊。

也算是没有完全照着标程改对的,挺开心的,保持好状态,加油


/***********
poj1947
2015.12.29
448K	32MS	C++	1553B
***********/
#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,p,tol;
int dp[200][200];
bool is[500];
struct node
{
    int to,next;
}edge[40000];
int head[40000];
void addedge(int u,int v)
{
    edge[tol].to=v;edge[tol].next=head[u];head[u]=tol++;
}
int min(int a,int b)
{
    if(a<b) return a;
    return b;
}
void dfs(int u)
{
    for(int i = 0; i <= p; i++) dp[u][i] = 0x3f3f3f3f;
    dp[u][1] = 0;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        dfs(v);
        for(int j = p; j >= 0; j--)
        {
            int tmp = dp[u][j]+1;
            for(int k = 1; k < j; k++)
                tmp = min(tmp, dp[u][k]+dp[v][j-k]);
            dp[u][j] = tmp;
        }
    }
}
int main()
{
    //freopen("cin.txt","r",stdin);
    while(~scanf("%d",&n))
    {
        scanf("%d",&p);
        memset(head,-1,sizeof(head));
      //  memset(cnt,0,sizeof(cnt));
        memset(is,false,sizeof(is));
        tol=0;
        for(int i=1;i<n;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            if(a<b) {addedge(a,b);is[b]=true;}
            else {addedge(b,a);is[a]=true;}
        }
        int r;
        for(int i=1;i<=n;i++)
        {
            if(!is[i]) {r=i;break;}
        }
        dfs(r);
        int ans;
        ans=dp[r][p];
        //for(int i=1;i<=n;i++) printf("i=%d dp=%d\n",i,dp[i][p]);
        for(int i = 1; i <= n; i++)
            ans = min(ans, dp[i][p]+1);
        printf("%d\n",ans);
    }
    return 0;
}