复习记忆化搜索和数位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精炼

posted @ 2021-11-08 18:08  juuich  阅读(68)  评论(0编辑  收藏  举报