题解 P3360 【偷天换日】

· · 题解

废话不说,直接开讲。

这道题和访问美术馆及其相似,就是加了01背包。。。 是一道不错的树形dp

我们可以采用线段树的存储思想 虽然我还不会线段树

我们把当前节点作为x,左节点就是2x,右节点即2x+1

这样的话,程序看上去,简单,直观多了

唯一的缺点就是耗空间,像我Re到飞起

对于dp,可以直接在读入中做。

还有这道题有两个坑点,一是小偷在s-1秒时就必须离

二是走廊必须走一来一回两遍

所以做dfs前s--,做背包时前t*2

背包容量下限加一个时间t(*2)

然后就和选课一样,最多时间s(-1),最少t(*2)

从左节点下限一秒也不花,上限走这条走廊的时间i-t(*2),我们有公式

dp[x][i]=max(dp[x][i],dp[x<<1][j]+dp[x<<1|1][i-j-t]);

x为当前节点,i为走该走廊的时间

左儿子花j秒,右儿子花i-j-t(2)秒,走走廊t(2)秒(t是读入时的t,可以在dp前就t*2

下面给出代码

#include<bits/stdc++.h>
using namespace std;
int s,dp[10010][10010],ans,a[2010],b[2010];
void read(int x){

    int t,k;
    cin>>t>>k;
    t=t<<1;
    if(k>0){
        for(int i=1;i<=k;i++){
            cin>>a[i]>>b[i];
        }   
        for(int i=1;i<=k;i++){
            for(int j=s;j>=t+b[i];j--){
                dp[x][j]=max(dp[x][j-b[i]]+a[i],dp[x][j]);
            }
        }
    }
    if(!k){
        read(x<<1);
        read(x<<1|1);
        for(int i=s;i>=t;i--)
            for(int j=0;j<=i-t;j++){
                dp[x][i]=max(dp[x][i],dp[x<<1][j]+dp[x<<1|1][i-j-t]);
            }
    }
}
int main()
{
    ios::sync_with_stdio(false);
    //freopen("steal.in","r",stdin);
    //freopen("steal.out","w",stdout);
    cin>>s;
    s--;
    read(1);
    cout<<dp[1][s];
    return 0;
}

不看美术馆的题解还想不到这样保存