poj1947Rebuilding Roads【树型dp 01背包】

这个题和前一篇很像啊,导致我昨天晚上冥思苦想结果误入歧途还自以为是。先说说哪里像吧:前一个题是说彼此之间有所属关系,贿赂一个人,整个子树都会给她投票,问至少需要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;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值