【编程题-错题集】分割等和子集(动态规划 - 01背包)

简介: 【编程题-错题集】分割等和子集(动态规划 - 01背包)


一、分析题目

01 背包 问题:将原问题转换成:从 n 个数中选,总和恰好为 sum / 2,看能否挑选出来。

  • 状态 dp[i][j] 表示:从前 i 个数中挑选,总和恰好为 j,能否凑成。
  • 状态转移方程:dp[i][j] = dp[i-1][j] || dp[i-1][j-arr[i]],第二个状态必须保证 j>= arr[i]
  • 初始化:dp[0][0] = true
  • 返回值:dp[n][sum/2]

二、代码

1、值得学习的代码

//牛客
#include <iostream>
using namespace std;
 
const int N = 510, M = 510 * 110 / 2;
 
int n;
int arr[N];
int dp[N][M];
 
int main()
{
    cin >> n;
    int sum = 0;
    for(int i = 1; i <= n; i++)
    {
        cin >> arr[i];
        sum += arr[i];
    }
 
    if(sum % 2 == 1) cout << "false" << endl;
    else
    {
        sum /= 2;
        dp[0][0] = true;
        for(int i = 1; i <= n; i++)
        {
            for(int j = 0; j <= sum; j++)
            {
                dp[i][j] = dp[i - 1][j];
                if(j >= arr[i])
                {
                    dp[i][j] = dp[i][j] || dp[i - 1][j - arr[i]];
                }
            }
        }
        if(dp[n][sum]) cout << "true" << endl;
        else cout << "false" << endl;
    }
 
    return 0;
}

2、力扣 AC 代码(推荐)

//二维dp
class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int n=nums.size();
        int sum=0;
        for(auto e:nums)
            sum+=e;
        if(sum%2==1)
            return false;
        int target=sum/2;
        vector<vector<int>> dp(n, vector<int>(target+1));
        for(int i=1; i<n; i++)
        {
            for(int j=0; j<=target; j++)
            {
                if(j>=nums[i])
                    dp[i][j]=max(dp[i-1][j], dp[i-1][j-nums[i]]+nums[i]);
                else
                    dp[i][j]=dp[i-1][j];
                if(dp[i][j]==target)
                    return true;
            }
        }
        return false;
    }
};
 
//一维dp
class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int n=nums.size();
        int sum=0;
        for(auto e:nums)
            sum+=e;
        if(sum%2==1)
            return false;
        int target=sum/2;
        vector<int> dp(20010);
        for(int i=1; i<n; i++)
        {
            for(int j=target; j>=nums[i]; j--)
            {
                dp[j]=max(dp[j], dp[j-nums[i]]+nums[i]);
                if(dp[j]==target)
                    return true;
            }
        }
        return false;
    }
};

三、反思与改进

刚拿到这道题就直接暴力破解了,竟然还通过了 90% 的样例,导致我一度怀疑是不是有哪个特殊样例没考虑到。对数组直接进行排序,然后对总和进行判断,奇数直接 false,偶数就取总和的一般 half 作为 目标值 target,接着遍历数组进行累加,刚好等于 half 则为 true,但这样做就忽略了所选的元素可能不是连续的这一点,所以这是错的。

一般遇到这种等于 target 值的题目可以考虑背包问题。


相关文章
|
4月前
|
算法
【动态规划专栏】背包问题:分割等和子集
【动态规划专栏】背包问题:分割等和子集
43 0
|
4月前
【题型总结】动态规划之按照某种形式分割数组以获得最值
【题型总结】动态规划之按照某种形式分割数组以获得最值
68 0
|
4月前
【每日一题Day146】给定行和列的和求可行矩阵 | 贪心
【每日一题Day146】给定行和列的和求可行矩阵 | 贪心
38 0
|
3月前
|
算法
【经典LeetCode算法题目专栏分类】【第4期】BFS广度优先算法:单词接龙、最小基因变化、二进制矩阵中的最短路径
【经典LeetCode算法题目专栏分类】【第4期】BFS广度优先算法:单词接龙、最小基因变化、二进制矩阵中的最短路径
|
4月前
【错题集-编程题】不相邻取数(动态规划 - 线性 dp)
【错题集-编程题】不相邻取数(动态规划 - 线性 dp)
|
4月前
|
存储
【错题集-编程题】合唱团(动态规划 - 线性 dp)
【错题集-编程题】合唱团(动态规划 - 线性 dp)
|
4月前
【编程题-错题集】连续子数组最大和(动态规划 - 线性 dp)
【编程题-错题集】连续子数组最大和(动态规划 - 线性 dp)
|
4月前
|
人工智能 JavaScript
【错题集-编程题】最大子矩阵(二维前缀和)
【错题集-编程题】最大子矩阵(二维前缀和)
|
4月前
【错题集-编程题】删除相邻数字的最大分数(动态规划 - 线性 dp)
【错题集-编程题】删除相邻数字的最大分数(动态规划 - 线性 dp)
|
4月前
|
算法 测试技术 C++
【状态压缩】【动态规划】【C++算法】691贴纸拼词
【状态压缩】【动态规划】【C++算法】691贴纸拼词