QBXT 2018春季DP&图论班 2018.5.2 --- 记忆化搜索

对于一些刚学DP的同学,直接写出DP转移方程是有些困难的。这里张浩威(张过亿)dalao教给了我们一种简便的方法。可以先写出简单的暴力搜索,然后转成记忆化搜索。

优点:思路简单。

缺点:代码略难写,且很难优化。

例:01背包问题

首先可以写出最暴力的做法,即枚举每个物品选还是不选。

void dfs(int x,int y,int z) //x=当前物品编号,y=当前体积之和,z=当前价值之和
{
  if (x==n+1)
  {
    if (y<=m) ans=max(ans,z);
    return;
  }
  dfs(x+1,y+w[x],z+v[x]);
  dfs(x+1,y,z);
}
dfs(1,0,0);
时间复杂度为 O(2^n)

可以观察到当x和y固定时,z越大越好。

考虑将x y当成状态 z当成状态所代表的值。即dp[x][y]=z。然后把DFS的返回值改为int。

int dfs(int x,int y){
    if(y>t) return -inf;
    if(x==n+1){
        return 0;
    }
    if(dp[x][y]) return dp[x][y];
    dp[x][y]=max(dfs(x+1,y),dfs(x+1,y+w[x])+v[x]);
    return dp[x][y];
}

x=当前物品编号 y=当前体积之和 dp[x][y]=接下来还能获取的最大价值 t=背包体积。

因为y>t的情况显然不合法,因此dp[x][y](y>t)= -inf  即这种情况绝对不会成为最优答案。

if(x==n+1) 递归边界 接下来还能获取的价值为0,因此返回0。

if(dp[x][y]) 说明之前曾经遍历过这种情况。因为该题的状态不会因为DFS的不同而改变,即DFS(3,2)与DFS(3,4)都有可能遍历到当前情况,但不会因DFS的不同而不同,因此可以直接返回之前被遍历到时的值。

dp[x][y]=max(dfs(x+1,y),dfs(x+1,y+w[x])+v[x]); 有两种决策:选与不选,因此dp[x][y]会有上面两个值转移到。需要注意,dp[][]表示的是接下来能获取的最大价值,所以别忘了加上当前的贡献。

最终答案为dfs(1,0)

这就是记忆化搜索。时间复杂度为O(nt),其中n=物品数,t=背包体积。因为dp的状态数共有n*t种,所以该记忆化搜索的时间复杂度为O(nt)。事实上,记忆化搜索的时间复杂度约为状态的数量。

 总结:

①记忆化搜索思路简单,但代码难写,且难以优化

②可以先写出暴力DFS,然后转成记忆化搜索。其中在DFS的参数中找出满足:“越大/小越好”、“方案总数”、“可行or不可行(0/1)”

的变量,把它们提出来,作为dp数组的值;其他的参数作为状态,并放入dp数组的维度中;改变DFS的返回值(越大/小越好、方案总数 的返回值为int/longlong 可行or不可行 的返回值为bool)

③记忆化搜索的时间复杂度约为状态的数量。但不一定为dp数组的各维度乘积

记忆化搜索=没优化的DP

posted @ 2018-05-02 21:55  dprswdr  阅读(272)  评论(0)    收藏  举报