数据结构与算法(C++)– 动态规划(Dynamic Programming)

版权声明: https://blog.csdn.net/Wang_Jiankun/article/details/82460562

动态规划(Dynamic Programming)


理解动态规划的好文:https://www.sohu.com/a/153858619_466939


1、基础

定义:递归算法经常会有一些重复的计算,导致程序效率低。动态规划就是解决这个问题的方法。

动态规划的组成部分:

  • 最优子结构:分解问题
  • 边界:递归的终止条件
  • 状态转移方程:递归的方程

动态规划的解题步骤:

  • 分解问题,得到状态转移方程
  • 暴力搜索,找到冗余,通常用纯递归
  • 存储已经计算过的结果,通常用数组和字典
  • 编程时通常采用自底向上思想(递推)

2、走台阶问题

题目:有一座10级台阶的楼梯,从下往上走,每跨一步只能向上1级或者2级台阶。要求用程序来求出一共有多少种走法。

递归解法:

int solve(int n)
{
    if(n < 1)
        return 0;
    if(n == 1)
        return 1;
    if(n == 2)
        return 2;

    return solve(n-1)+solve(n-2);
}

备忘录解法:

int solve(int n,HashMap<Interger, Interger>map)
{
    if(n < 1)
        return 0;
    if(n == 1)
        return 1;
    if(n == 2)
        return 2;

    if(map.contains(n))
        return map.get(n);
    else
    {
        int value = solve(n-1)+solve(n-2);
        map.put(n, value);
        return value;
    }
}

动态规划解法:

int solve(int n,HashMap<Interger, Interger>map)
{
    if(n < 1)
        return 0;
    if(n == 1)
        return 1;
    if(n == 2)
        return 2;

    int a = 1;
    int b = 2:
    int temp = 0;

    for(int i=3; i<=n; i++)
    {
        temp = a + b;
        a = b;
        b = temp;
    }

    return temp;
}

题目改为一次最多能走k阶台阶

思路:其实是一样的,只要把边界和递归式扩展到 k 即可

// 边界条件, 转化为总共 k 阶台阶,可以任意走,到达k阶有几种走法
if(n < k)
    return power(2, k-1)

// 状态转移方程
solve(n-1) + solve(n-2) + ..... solve(n-k)

3、偷商店问题

题目:偷商店,不能连续偷两家,求偷的总额最大

// 共有几家店,每家店金额
n = x
nums[n] = {x, x, x, x}

int solve(int n, int nums[])
{
    if(n == 0)
        return 0;
    if(n == 1)
        return nums[0];

    int result[n];
    result[0] = nums[0]
    result[1] = math.max(nums[0], nums[1])

    for(int i=2; i<n; i++)
        result[i] = math.max(nums[i] + nums[i-2], nums[i-1])

    return result[n-1];
}

4、01背包问题

题目: 给定 n 种物品和一个容量为 C 的背包,物品 i 的重量是 wi,其价值为 vi ,应该如何选择装入背包的物品,使得装入背包中的物品的总价值最大?

// 下标为0的元素,没有用到,置为0
int v[N]={0,8,10,6,3,7,2};
int w[N]={0,4,6,2,2,5,1};
int n=6,c=12;

int solve()
{
    // 需要知道的初始值是 m[i][0],m[0][j] 都等于0,所以数组都初始化为0
    int m[n][c] = {0};
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=c;j++)
        {
            if(j>=w[i])
                m[i][j]=max(m[i-1][j],m[i-1][j-w[i]]+v[i]);  
            else
                m[i][j]=m[i-1][j];
        }
    }
    return m[n][c]

可以进一步压缩空间,只用2xc大小的数组:

int m[2][c] = {0};

// 求新值时,只与当前行和前一行有关
m[i%2][j]=max(m[(i-1)%2][j],m[(i-1)%2][j-w[i]]+v[i%2]);  

猜你喜欢

转载自blog.csdn.net/Wang_Jiankun/article/details/82460562