poj 1947 Rebuilding Roads 树形DP

传送门
给出n个节点的一棵树,询问最少毁掉几条边才可以使一颗节点数为p的子树独立出来


分析:
感觉是树形DP(⊙o⊙)…
那么看看状态吧
f[i][j]代表使以i为根节点数为p的子树独立出来最少毁掉几条边>o< >_<
怎么转移呢 ?_?
首先,当我们dfs(root)时,还没有dfs任何一个子节点时,f[root][1]=0,这是显然的,以为当前这棵树只有root这一个节点
当我们dfs完第一个子节点时,f[root][1]++,这也是显然的(^__^) ……~~
然后,我们可以用to[i]和root的f已知数组来转移
f[root][j+k]=min(temp[j]+f[to[i]][k])
temp[j]是在未进行转移时的f[root][j],因为有可能用飞、当前子节点来更新还不如直接割掉当前子节点更优
(因为我是用-1来代表f数组未被更新过,以此来记录j和k枚举到哪里,所以要记录一下原始f数组,也可以记录son[root]来记录jk枚举到哪里,然后按照01背包倒着跑,就不用记录原始状态了O(∩_∩)O~~~~)
然后dfs完第二个子节点,第三个子节点……也是一样的道理,但是不同的是就不只是f[root][1]++了,而是每一个已经被更新的f[root][j]++


代码如下:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define MIN(i,j) (i<j?i:j)
using namespace std;
const int maxn=150+5;
int n,p,cnt,hd[maxn],to[maxn],nxt[maxn],f[maxn][maxn];
void add(int x,int y){
    to[cnt]=y;
    nxt[cnt]=hd[x];
    hd[x]=cnt++;
}
void dfs(int root){
    f[root][1]=0;
    for(int i=hd[root];i!=-1;i=nxt[i]){
        dfs(to[i]);
        int temp[maxn];
        memset(temp,-1,sizeof(temp));
        for(int j=1;f[root][j]!=-1;j++)
            temp[j]=f[root][j];
        for(int j=1;f[root][j]!=-1;j++)
            f[root][j]++;
        for(int j=1;temp[j]!=-1;j++)
            for(int k=1;f[to[i]][k]!=-1;k++)
                f[root][j+k]=min(f[root][j+k]==-1 ? 0x3f3f3f3f : f[root][j+k],temp[j]+f[to[i]][k]);
    }
}
int main(){
    scanf("%d%d",&n,&p),cnt=0;
    memset(hd,-1,sizeof(hd));
    for(int i=1,x,y;i<n;i++){
                cin>>x>>y;
                if(x>y) swap(x,y);
                add(x,y);
        }
    memset(f,-1,sizeof(f));
    dfs(1);
    int ans=0x3f3f3f3f;
    for(int i=1;i<=n;i++)
        if(f[i][p]!=-1)
            ans=MIN(f[i][p]+(i!=1),ans);
    cout<<ans<<endl;
    return 0;
} 

感谢YOUSIKI O(∩_∩)O~~


by >o< neighthorn

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值