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

浙公网安备 33010602011771号