【洛谷luogu】P3360-偷天换日(树形DP)

本文针对一个有根树的动态规划问题进行了解析,该问题涉及到边权、叶子节点上的物品价值及时间限制等元素。文章通过枚举法解决非叶子节点的问题,并给出了一段C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原题链接:https://www.luogu.org/problemnew/show/P3360

题目大意

给一个有根树,有边权,叶子节点有画可以偷,偷画和经过边需要时间,给定n时间,最多可以偷多少价值的画。

解法

这奇怪的数据格式真是给跪了,叶子节点可以背包,非叶子节点怎么办呢?暴力枚举!

#include <iostream>
#include <ctime>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>

#define forn(i, n) for (int i = 0; i < (int)(n); ++i)
#define for1(i, n) for (int i = 1; i <= (int)(n); ++i)
#define pb push_back

using namespace std;
typedef long long LL;
const int N=1000+5;
struct Node{
    int lchild,rchild,w,x;
}Tree[N];
int cnt=0;
int New(int w,int x){
    Tree[++cnt].w=w;
    Tree[cnt].x=x;
    Tree[cnt].lchild=-1;
    Tree[cnt].rchild=-1;
    return cnt;
}
int dp[300+5][30+5][600+5];
int Build(){
    int t,x,w,c;
    cin>>t>>x;
    int r=New(t,x);
    if(x>0){
        //dp[300][30][600]
    //	cout<<"r is "<<r<<endl;
        for(int i=1;i<=x;i++){
            cin>>w>>c;
            for(int j=1;j<=600;j++){
                if(j>=c) dp[r][i][j]=max(dp[r][i-1][j],dp[r][i-1][j-c]+w);
                else dp[r][i][j]=dp[r][i-1][j];
            //	cout<<dp[r][i][j]<<" ";
            }
    //		cout<<endl;
        }
    //	cout<<endl;
    }
    else if(x==0){
        Tree[r].lchild=Build();
        Tree[r].rchild=Build();
    }
    return r;
}

void dfs(int u){
    if(Tree[u].x>0) return ;
    int l=Tree[u].lchild;
    int r=Tree[u].rchild;
    dfs(l);
    dfs(r);
    int lx=(Tree[l].x>0?Tree[l].x:2);
    int rx=(Tree[r].x>0?Tree[r].x:2);
    int lw=Tree[l].w;
    int rw=Tree[r].w;
    for(int i=0;i<=600;i++){
        for(int j=0;j+i<=600;j++){
            dp[u][2][i+j]=max(dp[u][2][i+j],(i-2*lw>=0?dp[l][lx][i-2*lw]:0)+(j-2*rw>=0?dp[r][rx][j-2*rw]:0));
        }
    }
}


int main() {

#ifdef LOCAL_DEFINE
    freopen("input.in", "rt", stdin);
#endif
    int n;
    cin>>n;
    int r=Build();
    dfs(r);
    int rw=Tree[r].w;
    cout<<dp[r][2][n-2*rw-1]<<endl;

#ifdef LOCAL_DEFINE
    cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << " s.\n";
#endif
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值