动态规划——01背包问题

简介: 动态规划——01背包问题


题目

版本一(二维数组)

思路

1.定义状态 dp[ i ][ j ] ,表示前i件物品,背包容量为j时的最优解。(局部最优解)

2.计算dp[ i ][ j ]时会现两种情况:背包容量不够,背包容量足够。

 (1)背包容量不够:显然容量不够时就拿不了第i件物品,所以此时有dp[ i ][ j ] = dp[ i-1 ][ j ]

 (2)背包容量足够:此时会有两种先择,拿或不拿。

     1)拿第i件物品,此时最优解为dp[ i ][ j ] =dp[ i-1 ][ j-v[ i ] ] + w[ i ]

     2)不拿第i件物品,dp[ i ][ j ] = dp[ i-1 ][ j ]

     3)因为在做出的选择要使所拿物品价值最大

                故dp[ i ][ j ] = max {    dp[ i-1 ][ j-v[ i ] ] + w[ i ]    , dp[ i-1 ][ j ]    }(状态转移方程式)

题解

在完成题目要求的输入时,为使物品与数组下标对应,在输入数组v和w中的值时从1开始

for (i = 1; i <= N; i++)
    {
        scanf("%d%d", &v[i], &w[i]);
    }

给dp数组初始化,没有物品时,无论背包容量有多大,能装最大价值物品都为0;背包容量为0时,无论有多少件物品都装不了东西

初始化过程如下:

 //给dp数组初始化,没有物品时,无论背包容量有多大,能装最大价值物品都为0
    for (i = 0; i <= V; i++)
    {
        dp[0][i] = 0;
    }
    //给dp数组初始化,背包容量为0时,无论有多少件物品都装不了东西
    for (i = 0; i <= N; i++)
    {
        dp[i][0] = 0;
    }

核心过程如下:

int main()
{
    int i, j;
    int N, V;
    scanf("%d%d", &N, &V);
    int v[1001], w[1001];
    //为使物品与下标对应,所以输入时从1开始
    for (i = 1; i <= N; i++)
    {
        scanf("%d%d", &v[i], &w[i]);
    }
    int dp[1001][1001];
    //给dp数组初始化,没有物品时,无论背包容量有多大,能装最大价值物品都为0
    for (i = 0; i <= V; i++)
    {
        dp[0][i] = 0;
    }
    //给dp数组初始化,背包容量为0时,无论有多少件物品都装不了东西
    for (i = 0; i <= N; i++)
    {
        dp[i][0] = 0;
    }
    for (i = 1; i <= N; i++)
    {
        for (j = 1; j <= V; j++)
        {
            // 背包容量不足
            if (j < v[i])
                dp[i][j] = dp[i - 1][j];
            // 背包容量足够,可进行选择
            else
                dp[i][j] = fmax(dp[i - 1][j], dp[i - 1][j - v[i]] + w[i]);
        }
    }
    printf("%d", dp[i - 1][j - 1]);
}

完整代码:

int main()
{
    int i, j;
    int N, V;
    scanf("%d%d", &N, &V);
    int v[1001], w[1001];
    for (i = 1; i <= N; i++)
    {
        scanf("%d%d", &v[i], &w[i]);
    }
    int dp[1001] = { 0 };
    for (i = 1; i <= N; i++)
    {
        for (j = V; j >= 1; j--)
        {
            if (j >= v[i])
            {
                dp[j] = fmax(dp[j - v[i]] + w[i], dp[j]);
            }
        }
    }
    printf("%d", dp[V]);
}

版本二(一维数组)

上面的方法时间空间复杂度都为 O(N*V),时间复杂度很难继续优化。但是空间复杂度可以进一步优化。

思路

可以先看一看代码再看下面解析

(1)状态dp[ j ]定义:N件物品,背包容量 j 下的最优解。

(2)注意在次循环中枚举背包容量 j 必须从V开始。因为在用二维数组求解时会发现,算有前i件物品时的最优解的时候,只会与有前 i-1 件物品时的最优解有关,不会与有前 i-2 或者与有前 i+1件物品时有关。在使用一维数组求解时,当 i 更新了,数组内容仍然记录者之前的数据,随着 j 的更新,数组内容才会随着更新。在次循环中,若 j 正序,数组内容是从前往后更新的,而在计算dp[ j ]时正需要前面的数据,但是已经更新过找不到了。若 j 逆序,数组内容也是从后往前更新的,在计算dp[ j ]时,它前面的旧数据仍然存在,只有当旧数据被使用过后才会被更新。

(3)状态转移方程为:f[j] = max(f[j], f[j - v[i]] + w[i] )

代码

1. int main()
2. {
3. int i, j;
4. int N, V;
5. scanf("%d%d", &N, &V);
6. int v[1001], w[1001];
7. for (i = 1; i <= N; i++)
8.     {
9. scanf("%d%d", &v[i], &w[i]);
10.     }
11. int dp[1001] = { 0 };
12. for (i = 1; i <= N; i++)
13.     {
14. for (j = V; j >= 1; j--)
15.         {
16. if (j >= v[i])
17.             {
18.                 dp[j] = fmax(dp[j - v[i]] + w[i], dp[j]);
19.             }
20.         }
21.     }
22. printf("%d", dp[V]);
23. }

在看完上面解析后,可以自己尝试这用两种方法解决该题目,题目链接:01背包问题


相关文章
动态规划之01背包问题和完全背包问题
动态规划之01背包问题和完全背包问题
|
算法 C语言 C++
【动态规划】背包问题(01背包,完全背包)
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
128 0
动态规划——01背包问题、完全背包问题
动态规划——01背包问题、完全背包问题
93 0
动态规划:01背包问题
动态规划:01背包问题
95 0
|
算法 JavaScript 前端开发
动态规划-01背包
前言 动态规划和递归是一对CP,递归通过将大问题分解成小问题以求得答案,而动态规划则是通过求解小问题来组成大问题的解。虽然其本质都是先求小问题的解,但是问题的推算不同:
|
算法 决策智能
背包问题——01背包|完全背包 1
背包问题——01背包|完全背包
319 0
背包问题——01背包|完全背包 2
背包问题——01背包|完全背包
196 0