P2014 [CTSC1997] 选课

题目链接

题意:选课,选课数有限制,课程之间有依赖(树状),问能选的最大学分。

好歹是做出来了,虽然感觉写的很史。
我想的是树上背包。
lim[x]表示x的限制数量,它是目标M和子树大小的较小者(目标是尽可能减少背包大小)。dp[x][y]表示的是以x为根,大小为y的子树的学分最大值。然后父节点可以把孩子的dp作为物品,做01背包。
但是要注意,每个孩子只能取一次,也就是说,不能先在孩子的子树里取一个体积较小的“物品”,再取一个较大的“物品”。所以才会有ndp[]这个东西,以保证在孩子的子树里只取一次。
然后0号节点我也设为一门课,但是没有学分,所以dp[][]初值条件会和普通节点有所不同,详见代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
#define endl '\n'
const int MAX=310;
// const int MOD;
int N,M,ans=LONG_LONG_MIN;
int siz[MAX],val[MAX],lim[MAX];
//siz子树大小 val课程学分 lim子树可选课程上限
int dp[MAX][MAX];//树上背包
int ndp[MAX];//滚动用
vector<int> e[MAX];
void dfs1(int x,int fa){
    siz[x]=1;
    for(auto t:e[x]){
        if(t==fa)continue;
        dfs1(t,x);
        siz[x]+=siz[t];
    }
}
void dfs(int x,int fa){
    if(e[x].size()==0&&e[x][0]==fa){
        lim[x]=1;
        dp[x][1]=val[x];
        return;
    }
    lim[x]=min(siz[x],M);
    if(x){
        dp[x][1]=val[x];//普通初值条件
        for(auto t:e[x]){
            if(t==fa)continue;
            dfs(t,x);
            for(int i=0;i<=lim[x];++i) ndp[i]=dp[x][i];
            for(int wei=lim[t];wei;wei--){
                // if(x==1&&wei==2){
                //     for(int i=0;i<=lim[x];++i)cout<<ndp[i]<<" ";
                // }
                // cout<<endl;
                for(int bag=lim[x];bag>=wei+1;bag--){
                    ndp[bag]=max(ndp[bag],dp[x][bag-wei]+dp[t][wei]);
                }
                // if(x==1&&wei==2){
                //     for(int i=0;i<=lim[x];++i)cout<<ndp[i]<<" ";
                // }
                // cout<<endl;
            }
            for(int i=0;i<=lim[x];++i) dp[x][i]=ndp[i];
        }
    }
    else{
        dp[0][0]=0;//特殊初值条件
        for(auto t:e[x]){
            // if(t==fa)continue;
            dfs(t,x);
            for(int i=0;i<=lim[x];++i) ndp[i]=dp[x][i];
            for(int wei=lim[t];wei;wei--){
                for(int bag=lim[x];bag>=wei;bag--){
                    ndp[bag]=max(ndp[bag],dp[x][bag-wei]+dp[t][wei]);
                }
            }
            for(int i=0;i<=lim[x];++i) dp[x][i]=ndp[i];
        }
    }
    // cout<<x<<" ";
    // for(int i=1;i<=lim[x];i++){
    //     cout<<dp[x][i]<<" ";
    // }
    // cout<<endl;
}
void solve(){
    memset(dp,0xcf,sizeof dp);
    cin>>N>>M;
    for(int i=1;i<=N;i++){
        int s,k;cin>>s>>k;
        e[i].push_back(s);
        e[s].push_back(i);
        val[i]=k;
    }
    dfs1(0,-1);
    dfs(0,-1);
    // for(int i=0;i<=M;i++)ans=max(ans,dp[0][i]);
    cout<<dp[0][M]<<endl;
}
signed main() {
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    // int times;cin>>times;
    // while(times--)
    solve();
    return 0;
}

posted @ 2025-05-16 16:39  Treow  阅读(48)  评论(1)    收藏  举报