复习
- 动态规划的本质是递归。一切动态规划的问题一定能写成递归的形式。递归的本质就是将原问题拆解为子问题,子问题再推导出原问题。递归里每个参数的组合都表示一个状态。
- 动态规划两个重要性质:最优子结构和重叠子问题。用递归式来处理掉后效性,使它变成无后效性。用空间换时间去除冗余。
- 四个基本解题步骤:
3.1 设计暴力算法(递归算法),找到冗余
3.2 找到冗余之后,消除冗余(一维数组,二维数组,三维数组,Map)
3.3 把递归式整理成我们想要的状态转移方程
3.4 用自底向上的编程方式来解决问题
01背包问题
- 小偷由一个容量为W的背包,有n件物品,第 个物品价值 ,且重
- 目标:找到 使得对于所有的
- ,且 最大
- 套路:最大
- 暴力回溯怎么写?
#include <iostream>
using namespace std;
const int n = 5; //物品数量
const int W = 8; //总体积
int w[5] = {3, 5, 1, 2, 2};
int v[5] = {4, 5, 2, 1, 3};
int search(int idx, int S) //要一个一个考察放还是不放,就必须有一个变量参数存储考察到了哪一个物品
{ //另一个你放还是不放,取决于已经占用了多少的重量
if (S + w[idx] > W) //如果当前物品取不了,那么直接跳过
{
return 0;
}
if (idx >= n)
{
return 0;
}
return max(search(idx + 1, S + w[idx]) + v[idx], search(idx + 1, S));
}
int main()
{
cout << search(0, 0);
return 0;
}
-
表示前
件物品体积为
的最大价值。
记忆化搜索怎么写
#include <iostream>
using namespace std;
const int n = 5; //物品数量
const int W = 8; //总体积
int w[5] = {3, 5, 1, 2, 2};
int v[5] = {4, 5, 2, 1, 3};
int F[6][9];
int search(int idx, int S) //要一个一个考察放还是不放,就必须有一个变量参数存储考察到了哪一个物品
{ //另一个你放还是不放,取决于已经占用了多少的重量
if (S + w[idx] > W)
{
return 0;
}
if (idx >= n)
{
return 0;
}
if (F[idx][S] >= 0)
{
return F[idx][S];
}
F[idx][S] = max(search(idx + 1, S + w[idx]) + v[idx], search(idx + 1, S));
return F[idx][S];
}
int main()
{
for (int i = 0; i < 6; i++)
{
for (int j = 0; j < 9; j++)
{
F[i][j] = -1;
}
}
cout << search(0, 0);
return 0;
}
递推法怎么写呢
#include <iostream>
using namespace std;
const int n = 5; //物品数量
const int W = 8; //总体积
int w[5] = {3, 5, 1, 2, 2};
int v[5] = {4, 5, 2, 1, 3};
int F[n + 1][W + 1];
int search(int idx, int S)
{
if (S + w[idx] > W)
{
return 0;
}
if (idx >= n)
{
return 0;
}
for (int i = 0; i <= n; i++)
{
F[i][0] = 0;
}
for (int j = 0; j <= W; j++)
{
F[0][j] = 0;
}
for (int idx = 1; idx <= n; idx++)
{
for (int j = 1; j <= W; j++)
{
F[idx][j] = F[idx - 1][j];
if (j >= w[idx])
{
F[idx][j] = max(F[idx - 1][j - w[idx]] + v[idx], F[idx][j]);
}
}
}
return F[n][W];
}
int main()
{
cout << search(0, 0);
return 0;
}
可以节省空间么?
- 空间去冗余:滚动数组
#include <iostream>
using namespace std;
const int n = 5; //物品数量
const int W = 8; //总体积
int w[5] = {3, 5, 1, 2, 2};
int v[5] = {4, 5, 2, 1, 3};
int F[n + 1][W + 1];
int search(int idx, int S)
{
if (S + w[idx] > W)
{
return 0;
}
if (idx >= n)
{
return 0;
}
for (int i = 0; i <= n; i++)
{
F[i][0] = 0;
}
for (int j = 0; j <= W; j++)
{
F[0][j] = 0;
}
for (int idx = 1; idx <= n; idx++)
{
for (int j = 1; j <= W; j++)
{
F[idx % 2][j] = F[(idx - 1) % 2][j];
if (j >= w[idx])
{
F[idx % 2][j] = max(F[(idx - 1) % 2][j - w[idx]] + v[idx], F[idx % 2][j]);
}
}
}
return F[n % 2][W];
}
int main()
{
cout << search(0, 0);
return 0;
}
如果W太大怎么办?
- 要是用递推的话,时间复杂度是 ,那么没有办法优化了。那此时就用暴力搜索吧(复杂度为 )。