复习记忆化搜索和数位dp
大概是一年半前学的数位dp,当时用了个模板刷了不少题,自以为掌握了
但最近补去年上海站那道数位dp时发现自己只会套板子,原理只知皮毛
故回来连同记忆化搜索一块复习
参考博客:
聊聊动态规划与记忆化搜索
数字组成的奥妙——数位dp
记忆化搜索
由dp引入
P1048 [NOIP2005 普及组] 采药
经典01背包问题
暴力搜索,显然会超时
int ans=0;
void dfs( int pos , int lastv , int xans )
{
if(lastv<0) return;
if(pos>n)
{
ans=max(ans,tans);
return;
}
dfs(pos+1,tleft,tans);
dfs(pos+1,tleft-tcost[pos],tans+mget[pos]);
}
改进思路
1
用返回参数记录ans,可使搜索少一个参数
2
发现 { 遍历到pos时,剩余lastv } 是一个可重复利用的信息,
故引入\(f[i][j]\)表示遍历到i位置,剩余空间为j时的信息
int w[N],val[N];
int V,n;
int f[N][1005];
int dfs(int pos,int lastv)
{
if(pos>n) return f[pos][lastv]=0;//搜索完最后以为结束
if(f[pos][lastv]!=-1) return f[pos][lastv];//如果有可用信息则直接返回
int x1=-inf,x2=-inf;
if(lastv>=w[pos]) x1=dfs(pos+1,lastv-w[pos])+val[pos];
x2=dfs(pos+1,lastv);
return f[pos][lastv]=max(x1,x2);//返回并记录该状态
}
int main()
{
scanf("%d%d",&V,&n);
for(int i=1;i<=n;i++) scanf("%d%d",&w[i],&val[i]);
for(int i=1;i<=n;i++)for(int j=0;j<=V;j++)
{
f[i][j]=-1;
}
printf("%d",dfs(1,V));
return 0;
}
总结一下什么是记忆化搜索:
1
不依赖任何 外部变量
2
答案以返回值的形式存在, 而不能以参数的形式存在(就是不能将 dfs 定义成 dfs(pos ,tleft , nowans )dfs(pos,tleft,nowans), 这里面的 nowans 不符合要求).
3
对于相同一组参数, dfs 返回值总是相同的
优缺点
优
记忆化搜索可以避免搜到无用状态, 特别是在有状态压缩时
不需要注意转移顺序
边界情况非常好处理, 且能有效防止数组访问越界
有些 dp(如区间 dp)用记忆化搜索写很简单但正常 dp 很难
记忆化搜索天生携带搜索天赋,可以使用技能"剪枝"!
缺
很难有优化
无法使用滚动数组
递归可能会T
代码写起来不如dp精炼