背包类树形DP 选课题解

题目传送门;

我觉得题目给出0节点作为虚拟课程,也避免了我们要去想将若干个森林建成一棵树;将N个节点的森林建成了N+1条边的树;

其次,我们对这个题进行一个分析;

很容易想到F【x,t】表示以x为根的子树中,选择t门课程所获得得最高学分;

在x的子树中选择节点y,再以y为根的子树中,选择c_i门课程,保证Σc_i = t - 1;

初始状态,t=0时,F【x,t】=0;

通过分析状态转移方程,该方程实际上是一个分组背包模型,第i组的第j个物品体积为j,价值为F[y,j],背包总容积t-1;

我们要从每组中选择不超过1个物品(每个子节点y都只能选择一个状态转移到x),在选择总物品不超过t-1的前提下,学分最大;

是背包与树形DP的结合;

 

#include<bits/stdc++.h>
using namespace std;
int lin[1000],tot,n,m,x,f[400][400],s[400];
template<typename T>inline void read(T &x)
{
    x=0;T f=1,ch=getchar();
    while(!isdigit(ch))  {if(ch=='-')  f=-1;  ch=getchar();}
    while(isdigit(ch))  {x=x*10+ch-'0';  ch=getchar();}
    x*=f;
}
struct gg {
    int y,next;
}a[2000];
inline void add(int x,int y) {
    a[++tot].y=y;
    a[tot].next=lin[x];
    lin[x]=tot;
} 
inline void dp(int x) {
    f[x][0]=0;
    for(int i=lin[x];i;i=a[i].next) {
        int y=a[i].y;
        dp(y);
        //倒序循环当前选课总门数,或者背包的总体积; 
        for(int t=m;t>=0;--t) {
            //循环更深子树上的选课门数(组内物品);
            //此处倒序是为了正确处理组内体积为0的物品,这样可以从初始状态f【x,0】转移;
            for(int j=t;j>=0;--j) {
                if(t-j>=0) {
                    f[x][t]=max(f[x][t],f[x][t-j]+f[y][j]);
                }
            }
        }
    }
    if(x!=0) {//x!=0,选修x本身需要占用1节课,获得相应的学分; 
        for(int t=m;t>0;t--) 
            f[x][t]=f[x][t-1]+s[x];
    }
}
int main() {
    read(n);read(m);
    for(int i=1;i<=n;i++) {
        read(x);
        add(x,i);
        read(s[i]);
    }
    dp(0);
    cout<<f[0][m]<<endl;
    return 0;
}

 

转载于:https://www.cnblogs.com/Tyouchie/p/10830072.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值