动态规划求解数组划分问题
题目描述
给定一个整数数组,求是否可以把这个数据划分为和相等的两个部分。
输入输出样例
输入是一个一维整数数组,输出是一个布尔值,表示是否有满足题目条件的划分方式
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,满足题目条件。