31. 动态规划

一、什么是动态规划

  动态规划的基本原理是将问题分解成若干个子问题,通过解决子问题并将结果保存起来,从而避免重复计算,提高算法的效率。动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中,可能会有许多可行解,每一个解都对应于一个值,希望找到具有最优值的解。

  动态规划算法与分治法类似,其基本思想也是 将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的,即下一个子阶段的求解是建立在上一个子阶段的解的基础上,进行进一步的求解。

  动态规划可以通过 填表 的方式来逐步推进,得到最优解。

二、背包问题

  背包问题(Knapsack Problem)是一种组合优化的 NP 完全问题。该问题的描述是:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,如何选择物品才能使得物品的总价格达到最高。这个问题得名于如何选择最合适的物品放入一个给定背包的场景。

  假设有一个背包,容量为 4 磅,现有如下物品:

物品 重量 价值
吉他 1 1500
音响 4 3000
电脑 3 2000

  要求装入背包中的物体的价值最大,且重量没有超过。装入背包的物体不能重复。

背包问题

#include <stdio.h>

typedef struct Good
{
    char name[10];
    int weight;
    int value;
} Good;

void Knapsack(Good goods[], int n, int capacity);

int main(void)
{
    Good goods[] = {
        {"吉他", 1, 1500},
        {"音响", 4, 3000},
        {"电脑", 3, 2000},
    };
    int capacity = 4;
    int n = sizeof(goods) / sizeof(Good);

    Knapsack(goods, n, capacity);

    return 0;
}
/**
 * @brief 0-1背包问题的动态规划算法
 * 
 * @param goods 物品数组
 * @param n 物品种类数
 * @param capacity 背包容量
 */
void Knapsack(Good goods[], int n, int capacity)
{
    // v[i][j]表示在前i个物品中能够装入容量为j的背包的最大价值
    int v[n][capacity+1];
    int path[n][capacity+1];

    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < capacity + 1; j++)
        {
            v[i][j] = 0;
            path[i][j] = 0;
        }
    }

    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < capacity + 1; j++)
        {
            // j表示当前的背包容量
            if (goods[i].weight > j)                                            // 新增的物品重量大于背包容量
            {
                // i==0,表示是第一个物品,没有前一个物品,价值为0
                // v[i - 1][j]表示前i-1个物品装入容量为j的背包的最大价值
                v[i][j] = (i == 0) ? 0 : v[i - 1][j];
            }
            else                                                                // 新增的物品重量小于等于背包容量
            {
                // i==0,表示是第一个物品,没有前一个物品,价值为物品价值
                if (i == 0)
                {
                    v[i][j] = goods[i].value;
                    path[i][j] = 1;
                    continue;
                }
                // j - goods[i].weight表示去除当前物品的重量后背包剩余的容量
                // v[i - 1][j - goods[i].weight]表示前i-1个物品装入容量为(j - goods[i].weight的背包的最大价值
                // v[i - 1][j]表示前i-1个物品装入容量为j的背包的最大价值
                if (v[i - 1][j - goods[i].weight] + goods[i].value > v[i - 1][j])
                {
                    v[i][j] = v[i - 1][j - goods[i].weight] + goods[i].value;
                    path[i][j] = 1;
                }
                else
                {
                    v[i][j] = v[i - 1][j];
                }
            }
        }
    }
  
    for (int i = n - 1, j = capacity; i >= 0 && j > 0; i--)
    {
        if (path[i][j])
        {
            printf("%s 放入到背包\n", goods[i].name);
            j -= goods[i].weight;
        }
    }
}
posted @ 2023-08-14 20:35  星光樱梦  阅读(4)  评论(0编辑  收藏  举报