• 动态规划 —— 01背包问题


    01背包问题

    有n个重量和价值分别为wi,vi的物品。
    从这些物品中挑选出总重量不超过W的物品,
    求所有挑选方案中价值总和的最大值。

    针对每个物品是否放入背包进行搜索

    #include <iostream>
    
    #define MAX_N 100
    
    using namespace std;
    
    int n, W;
    int w[MAX_N], v[MAX_N];
    
    int rec(int i, int j) {      //从第i个物品开始挑选总重量小于j的部分
        int res;
    
        if (i == n) res = 0;    //已经没有剩余物品了
        else if (j < w[i]) res = rec(i + 1, j);     //无法挑选这个物品
        else res = max(rec(i + 1, j), rec(i + 1, j - w[i]) + v[i]);      //挑选和不挑选的两种情况都尝试一下
    
        return res;
    }
    
    int main() {
        cin >> n;
        for (int i = 0; i < n; i++)
            cin >> w[i] >> v[i];
        cin >> W;
        cout << rec(0, W) << endl;
        return 0;
    
    

    这种方法的搜索深度是n,而且每一层的搜索都需要两次分支,最坏就需要O(2n)的时间,当n比较大的时候就没办法解了。

    为了避免产生重复计算采用记忆化搜索

    #include <iostream>
    #include <cstring>
    
    #define MAX_N 100
    #define MAX_W 100
    
    using namespace std;
    
    int n, W;
    int w[MAX_N], v[MAX_N];
    int dp[MAX_N + 1][MAX_W + 1];
    
    int rec(int i, int j) {
        if (dp[i][j]) return dp[i][j];  //已经计算过的话直接使用之前的结果
    
        int res;
    
        if (i == n) res = 0;
        else if (j < w[i]) res = rec(i + 1, j);
        else res = max(rec(i + 1, j), rec(i + 1, j - w[i]) + v[i]);
    
        return dp[i][j] = res;
    }
    
    int main() {
        cin >> n;
        for (int i = 0; i < n; i++)
            cin >> w[i] >> v[i];
        cin >> W;
        memset(dp, 0, sizeof(dp));
        cout << rec(0, W);
        return 0;
    }
    

    对于同样的参数,只会在被调用到时执行递归部分,第二次之后都会直接返回。参数的组合不过nW种,而函数内只调用2次递归,所以只需要O(nW)的复杂度就能解决。

    DP数组

    记dp[i][j]根据rec的定义,从第i个物品开始挑选总重小于j时总价值的最大值。

    递推公式:
    dp[n][j]=0
    if(j<w[i]) dp[i][j]=dp[i+1][j]
    else dp[i][j]=max(rec(i+1,j),rec(i+1,j-w[i])+v[i])

    #include <iostream>
    
    #define MAX_N 100
    #define MAX_W 100
    
    using namespace std;
    
    int n, W;
    int w[MAX_N], v[MAX_N];
    int dp[MAX_N + 1][MAX_W + 1];
    
    int main() {
        cin >> n;
        for (int i = 0; i < n; i++)
            cin >> w[i] >> v[i];
        cin >> W;
        for (int i = n - 1; i > -1; i--) {
            for (int j = 0; j < W + 1; j++) {
                if (j < w[i]) dp[i][j] = dp[i + 1][j];
                else dp[i][j] = max(dp[i + 1][j], dp[i + 1][j - w[i]] + v[i]);
            }
        }
        cout << dp[0][W];
        return 0;
    }
    
  • 相关阅读:
    学习java的第二天
    第一天学习JAVA
    java小知识字符串,比较object,equalsIgnoreCase()拼接concat /截取substring
    java小知识api Scanner Random ArrayList
    java小知识对象
    java小知识数组
    java小知识方法
    java基础知识循环语句
    java小知识语句
    java基础小知识
  • 原文地址:https://www.cnblogs.com/AlexKing007/p/12338305.html
Copyright © 2020-2023  润新知