动态规划笔记(三)

复习

  1. 动态规划的本质是递归。一切动态规划的问题一定能写成递归的形式。递归的本质就是将原问题拆解为子问题,子问题再推导出原问题。递归里每个参数的组合都表示一个状态。
  2. 动态规划两个重要性质:最优子结构和重叠子问题。用递归式来处理掉后效性,使它变成无后效性。用空间换时间去除冗余。
  3. 四个基本解题步骤:
    3.1 设计暴力算法(递归算法),找到冗余
    3.2 找到冗余之后,消除冗余(一维数组,二维数组,三维数组,Map)
    3.3 把递归式整理成我们想要的状态转移方程
    3.4 用自底向上的编程方式来解决问题

01背包问题

  • 小偷由一个容量为W的背包,有n件物品,第 i i 个物品价值 v i v_i ,且重 w i w_i
  • 目标:找到 x i x_i 使得对于所有的 x i   =   { 0 , 1 } x_i~=~\{0,1\}
  • s u m ( w i x i ) < = W sum(w_i*x_i) <= W ,且 s u m ( x i v i ) sum(x_i*v_i) 最大
  • 套路:最大
    • 暴力回溯怎么写?
#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;
}
  • F ( i , W ) F(i,W) 表示前 i i 件物品体积为 W W 的最大价值。
    记忆化搜索怎么写
#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太大怎么办?

  • 要是用递推的话,时间复杂度是 O ( n W ) O(n*W) ,那么没有办法优化了。那此时就用暴力搜索吧(复杂度为 O ( 2 n ) O(2^n) )。

猜你喜欢

转载自blog.csdn.net/qq_39504764/article/details/89946903
今日推荐