题解 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;
}
不看美术馆的题解还想不到这样保存