【动态规划】01背包问题

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

动态规划(背包问题)

1. 01背包

题目链接

  1. 状态表示
    dp[i][j] 表示从前i个物品当中挑选,总体积不超过j,所有选法当中能挑选出来的最大价值
  2. 状态转移方程

  1. 初始化
  2. 填表
  3. 返回值

AC代码:

#include <iostream>
#include <cstring>
const int N = 1010;
int n, V, v[N], w[N];
int dp[N][N];
int main()
{
    std::cin>> n >> V;
    for (int i = 1; i <= n; i++)
    {
        std::cin>> v[i] >> w[i];
    }
    for (int i = 1; i <= n; i++)
    {
        for (int j = 0; j <= V; j++)
        {
            dp[i][j] = dp[i - 1][j];
            if (j >= v[i]) {
                dp[i][j] = std::max(dp[i][j], w[i] + dp[i - 1][j - v[i]]);
            }
        }
    }
    std::cout << dp[n][V] << std::endl;
    memset(dp, 0, sizeof(dp));
    for (int j = 1; j <= V; j++) dp[0][j] = -1;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 0; j <= V; j++)
        {
            dp[i][j] = dp[i - 1][j];
            if (j >= v[i] && dp[i - 1][j - v[i]] != -1) 
            {
                dp[i][j] = std::max(dp[i][j], w[i] + dp[i - 1][j - v[i]]);
            }
        }
    }
    std::cout << (dp[n][V] == -1 ? 0 : dp[n][V]) << std::endl;
    return 0;
}

空间优化:

利用滚动数组做空间上的优化,遍历顺序需要从右到左

不需要解释优化后的状态表示,以及状态转移方程

优化后代码:

#include <iostream>
#include <cstring>
const int N = 1010;
int n, V, v[N], w[N];
int dp[N];
int main()
{
    std::cin>> n >> V;
    for (int i = 1; i <= n; i++)
    {
        std::cin>> v[i] >> w[i];
    }
    for (int i = 1; i <= n; i++)
    {
        for (int j = V; j >= v[i]; j--)
        {
            dp[j] = std::max(dp[j], w[i] + dp[j - v[i]]);
        }
    }
    std::cout << dp[V] << std::endl;
    memset(dp, 0, sizeof(dp));
    for (int j = 1; j <= V; j++) dp[j] = -1;
    for (int i = 1; i <= n; i++)
    {
        for (int j = V; j >= v[i]; j--)
        {
            if (dp[j - v[i]] != -1) 
            {
                dp[j] = std::max(dp[j], w[i] + dp[j - v[i]]);
            }
        }
    }
    std::cout << (dp[V] == -1 ? 0 : dp[V]) << std::endl;
    return 0;
}

2. 分割等和子集

题目链接

将一个数组分割成相同的两部分,就需要在整个数组里面找正好相等就可以。其实就是一个背包问题

  1. 状态表示
    dp[i][j]表示 0 到 i 区间内正好等于是否可以满足正好等于 j
  2. 状态转移方程

  1. 初始化
    第一列为true ,当目标是0是肯定可以满足
  2. 填表
  3. 返回值

AC代码:

class Solution 
{
public:
    bool canPartition(vector<int>& nums) 
    {
        int n = nums.size();
        int sum = 0;
        for (auto x : nums) sum += x;
        if (sum % 2) return false;
        int aim = sum / 2;
        vector<vector<bool>> dp(n + 1, vector<bool>(aim + 1));
        for (int i = 0; i <= n; i++) dp[i][0] = true;
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= aim; j++)
            {
                dp[i][j] = dp[i - 1][j];
                if (j >= nums[i - 1]) 
                    dp[i][j] = dp[i][j] || dp[i - 1][j - nums[i - 1]];
            }
        }
        return dp[n][aim];
    }
};

利用滚动数组进行优化:

class Solution 
{
public:
    bool canPartition(vector<int>& nums) 
    {
        int n = nums.size();
        int sum = 0;
        for (auto x : nums) sum += x;
        if (sum % 2) return false;
        int aim = sum / 2;
        vector<bool> dp(aim + 1);
        dp[0] = true;
        for (int i = 1; i <= n; i++)
        {
            for (int j = aim; j >= nums[i - 1]; j--)
            {
                dp[j] = dp[j] || dp[j - nums[i - 1]];
            }
        }
        return dp[aim];
    }
};

3. 目标和

题目链接

分析题目,a 代表所有正数的和,b则代表所有负数的和

a - b = target a + b = sum 所以a = (target + sum) / 2

所以最终求的是是否可以让这个数是a

  1. 状态表示
    dp[i][j]表示从 i 个中选正好等于j 有多少中选法
  2. 状态转移方程

  3. 初始化
  4. 填表
  5. 返回值

AC代码:

class Solution 
{
public:
    int findTargetSumWays(vector<int>& nums, int target) 
    {
        int sum = 0;
        for (auto x : nums) sum += x;
        int aim = (sum + target) / 2;
        if (aim < 0 || (sum + target) % 2) return 0;
        int n = nums.size();
        vector<vector<int>> dp(n + 1, vector<int>(aim + 1));
        dp[0][0] = 1;
        for (int i = 1; i <= n; i++)
        {
            for (int j = 0; j <= aim; j++)
            {
                dp[i][j] = dp[i - 1][j];
                if (j >= nums[i - 1]) 
                {
                    dp[i][j] += dp[i - 1][j - nums[i - 1]];
                }
            }
        }
        return dp[n][aim];
    }
};

4. 最后一块石头的重量 ||

题目链接

这个题目就是在一个数组当中选一些数字,让这些数字尽可能的接近sum / 2

  1. 状态表示
    dp[i][j]表示 i 中选,总体积不超过j此时的最大和
  2. 状态转移方程

  1. 初始化
  2. 填表
  3. 返回值

AC代码:

class Solution {
public:
    int lastStoneWeightII(vector<int>& stones) {
        int sum = 0;
        for (auto x : stones) sum += x;
        int n = stones.size(), m = sum / 2;
        vector<vector<int>> dp(n + 1, vector<int>(m + 1));
        for (int i = 1; i <= n; i++) 
        {
            for (int j = 0; j <= m; j++)
            {
                dp[i][j] = dp[i - 1][j];
                if (j >= stones[i - 1]) 
                {
                    dp[i][j] = max(dp[i][j], dp[i - 1][j - stones[i - 1]] + stones[i - 1]);
                }
            }
        }
        return sum - 2 * dp[n][m];
    }
};
相关文章
并发与并行的区别(详细介绍)
并发与并行的区别(详细介绍)
11051 0
|
SQL 关系型数据库 MySQL
案例剖析:MySQL唯一索引并发插入导致死锁!
案例剖析:MySQL唯一索引并发插入导致死锁!
857 0
案例剖析:MySQL唯一索引并发插入导致死锁!
|
算法 搜索推荐 Java
解析01背包问题及其在动态规划中的应用
解析01背包问题及其在动态规划中的应用
|
6月前
|
SQL 算法 数据挖掘
【SQL周周练】:利用行车轨迹分析犯罪分子作案地点
【SQL破案系列】第一篇: 如果监控摄像头拍下了很多车辆的行车轨迹,那么如何利用这些行车轨迹来分析车辆运行的特征,是不是能够分析出犯罪分子“踩点”的位置
210 15
|
算法 安全 数据库
数据库死锁的解决方案有哪些?
【10月更文挑战第28天】数据库死锁是数据库管理中的一个常见问题
590 71
|
并行计算 Java API
写出高效率python的90个方法,附案例(python3经典编程案例)
该文章提供了90个提高Python编程效率的方法及案例,旨在帮助开发者编写更加高质量和优化的Python代码。
262 1
|
机器学习/深度学习 算法 计算机视觉
探索深度学习在图像识别中的应用及挑战
本文深入探讨了深度学习技术在图像识别领域的应用,并分析了其面临的主要挑战。通过实例和数据分析,本文旨在揭示深度学习如何推动图像识别技术的发展,同时指出当前技术的局限性和未来的发展方向。
129 27
|
运维 监控 安全
系统故障排查与问题解决指南:步步为营,精准定位
【8月更文挑战第16天】系统故障排查与问题解决是一项复杂而艰巨的任务,需要运维人员具备扎实的专业知识、丰富的实践经验以及良好的沟通能力和团队合作精神。通过遵循本文提供的指南,您可以更加高效地应对系统故障挑战,保障系统的稳定运行和业务的持续发展。
|
人工智能 算法 决策智能
动态规划之背包问题(01背包问题、完全背包问题、方案数填满型背包问题)
动态规划之背包问题(01背包问题、完全背包问题、方案数填满型背包问题)
642 1
|
运维 关系型数据库 MySQL
在Linux中,如何使用strace进行故障排查?
在Linux中,如何使用strace进行故障排查?