偷天换日 树形DP+背包DP
老规矩先上题面链接
偷天换日
思路
首先拿到题我们就会发现这题是一个树形结构
在每一个叶节点有一个类似0/1背包的问题
显然0/1背包比树形结构好跑
由于树形结构的DFS性质,这题的主要框架注定会是记搜
并且,拿到树形结构为问题的时候,我们就可以以子树划分阶段了
然后在每个叶节点及展厅跑一个0/1背包即可
DP设计
依惯例,我们对DP的设计进行一个详解:
1.阶段:
对于某个非叶节点的节点来说:以子树为阶段
对于某个非叶节点来说:以每一幅画为状态
2.状态:
对于时间花销问题:
本题时间范围较小,可以以时间花销为状态进行DP
对于遍历边和进行0/1来说,都是需要时间花销的,
因此以时间为状态可以通用于两部分DP
3.决策:
对于某个非叶节点的节点来说:往哪个方向走就是决策
对于某个非叶节点来说:偷哪幅画就是决策
4.状态转移方程:
树形:f[u][i]=std::max(f[u][i],f[u<<1][i-j-x]+f[u<<1|1][j]);
0/1:f[u][j]=std::max(f[u][j],f[u][j-c[i]]+v[i]);
实现细节
1.针对输入DFS序的问题:
在进行记搜(递归)的过程中实现输入即可
2.针对叶节点进行0/1DP的问题:
注意循环到的成本最小值要加上来去的成本,否则时间的开销会有偏差
3.针对边权的问题:
每条要跑的边都需要跑两次,因此边权乘2
4.针对逃跑时间的问题:
逃跑时间最晚n-1,如果是n代表主人公会和警察在第n时刻恰好相遇
Code
偷天换日
#include <bits/stdc++.h>
int n,m,a,b;
const int o=5700;
int v[o],f[o][o],c[o];
namespace mf{
int read(){
int x=1,y=0;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-'){
x=-1;
}
ch=getchar();
}
while(isdigit(ch)){
y=(y<<1)+(y<<3)+(ch&15);
ch=getchar();
}
return x*y;
}
void work(int u){
int x,y;
x=read();
y=read();
x<<=1;
if(y){
for(int i=1;i<=y;i++){
v[i]=read();
c[i]=read();
}
for(int i=1;i<=y;i++){
for(int j=n;j>=c[i]+x;j--){
f[u][j]=std::max(f[u][j],f[u][j-c[i]]+v[i]);
}
}
}
else{
work(u<<1);
work(u<<1|1);
for(int i=x;i<=n;i++){
for(int j=0;j<=i-x;j++){
f[u][i]=std::max(f[u][i],f[u<<1][i-j-x]+f[u<<1|1][j]);
}
}
}
}
void in(){
n=read();
n--;
}
void out(){
printf("%d",f[1][n]);
}
}
int main(){
freopen("steal.in","r",stdin);
freopen("steal.out","w",stdout);
mf::in();
mf::work(1);
mf::out();
return 0;
}

浙公网安备 33010602011771号