动态规划求解数组划分问题

题目描述


       给定一个整数数组,求是否可以把这个数据划分为和相等的两个部分。

输入输出样例

       输入是一个一维整数数组,输出是一个布尔值,表示是否有满足题目条件的划分方式

Input: [5, 1, 5,11]

Output: true

      满足题目条件的划分方式:[5, 1, 5]和[11]


思路:

      本题等价于一个0-1背包问题,设所有数字的总和为sum,我们的目标是选取一部分商品,使得他们的和为sum/2,这里不需要考虑价值,因此只需要用一个布尔矩阵dp表示状态矩阵,dp[i][j]表示前i个数的和是否为j,转移状态方程为dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i - 1]],即:

(1)如果前i-1个数之和可以为j(dp[i - 1][j]),则前i个数之和肯定也能为j;

(2)如果前i-1个数之和不可以为j,则判断前i-1个数之和是否能为j-nums[i-1](nums[i-1]其实就是数组第i个值),如果能,则前i-1个数加上第i个数正好为j,则dp[i][j]为true。

代码:

 

#include<vector>
#include<iostream>
#include<numeric>

using namespace std;

bool canPartition(vector<int>& nums) {
    int sum = accumulate(nums.begin(), nums.end(), 0);
    //如果sum为奇数,则不可能划分为两部分
    if (sum % 2) return false;
    int target = sum / 2, n = nums.size();
    vector<vector<bool>> dp(n + 1, vector<bool>(target + 1, false));
    //使用bool矩阵表示转移状态矩阵
    for (int i = 0; i <= n; ++i) {
        //边界条件--保证第i行,j=nums[i-1]列对应的值为True,即它本身
        dp[i][0] = true;
    }
    for (int i = 0; i <= n; ++i) {
        for (int j = nums[i - 1]; j <= target; ++j) {
            dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i - 1]];
        }
    }
    return dp[n][target];
}

int main() {
    vector<int> nums = { 5, 1, 5, 11 };
    bool result = canPartition(nums);
    cout << result << endl;
}

 

       状态矩阵如下:

[[T,  F,  F,  F,  F,  F,  F,  F,  F,  F,  F,  F],]

 [T,  F,  F,  F,  F,  T,  F,  F,  F,  F,  F,  F],

 [T,  T,  F,  F,  F,  T,  T,  F,  F,  F,  F,  F],

 [T,  T,  F,  F,  F,  T,  T,  F,  F,  F,  F,  T],

 [T,  T,  F,  F,  F,  T,  T,  F,  F,  F,  F,  T]]

        dp[4][11]=T,则4个数之和可以为11,满足题目条件。

 

posted @ 2021-09-25 15:21  ChangYuanD  阅读(115)  评论(0编辑  收藏  举报