动态规划------平均切分数组之和为两部分

题目描述:

Given a non-empty array containing only positive integers, find if the array can be partitioned into two subsets such that the sum of elements in both subsets is equal.

Note:

    1. Each of the array element will not exceed 100.
    2. The array size will not exceed 200.

示例:

Input: [1, 5, 11, 5]

Output: true

Explanation: The array can be partitioned as [1, 5, 5] and [11].
思路:一个背包的题目,背包容量为数组中元素和的一半+1,这样只要看是否有元素可以正好填满背包即可.
但是每个元素只能用一次,所以在尝试放一个元素的时候还要避免他对尝试放其他位置时对自己的影响.
所以在尝试放一个元素到背包的时候需要从容量最大的位置开始,如果(当前位置-当前元素大小)位置可以通过放置之前的元素达到,
则当前位置也可以通过放置当前元素正好达到这个位置.状态转移方程为:dp[i] = dp[i] || dp[i - nums[k]];
代码如下所示,但是该算法中的dp是啥?
  1. class Solution {  
  2. public:  
  3.     bool canPartition(vector<int>& nums) {  
  4.         int sum = accumulate(nums.begin(), nums.end(), 0);  
  5.         if(sum&1) return false;  
  6.         vector<int> dp(sum/2+1, 0);  
  7.         for(int i = 0, dp[0] = 1; i < nums.size(); i++)  
  8.         {  
  9.             for(int j = sum/2; j >= nums[i]; j--)  
  10.                 dp[j] = dp[j] || dp[j-nums[i]];  
  11.         }  
  12.         return dp[sum/2];  
  13.     }  
  14. }; 提交在leetcode上面,编译没通过。

查看另外一个人的博客,发现解释的更为仔细,具体如下:

这道题给了我们一个数组,问我们这个数组能不能分成两个非空子集合,使得两个子集合的元素之和相同。那么我们想,原数组所有数字和一定是偶数,不然根本无法拆成两个和相同的子集合,那么我们只需要算出原数组的数字之和,然后除以2,就是我们的target,那么问题就转换为能不能找到一个非空子集合,使得其数字之和为target。开始我想的是遍历所有子集合,算和,但是这种方法无法通过OJ的大数据集合。于是乎,动态规划DP就是我们的不二之选。我们定义一个一维的dp数组,其中dp[i]表示数字i是否是原数组的任意个子集合之和,那么我们我们最后只需要返回dp[target]就行了。我们初始化dp[0]为true,由于题目中限制了所有数字为正数,那么我们就不用担心会出现和为0或者负数的情况。那么关键问题就是要找出递归公式了,我们需要遍历原数组中的数字,对于遍历到的每个数字nums[i],我们需要更新我们的dp数组,要更新[nums[i], target]之间的值,那么对于这个区间中的任意一个数字j,如果dp[j - nums[j]]为true的话,那么dp[j]就一定为true,于是地推公式如下:

dp[j] = dp[j] || dp[j - nums[i]]         (nums[i] <= j <= target)   但是某个dp[x]为真,在程序中我们要如何表示呢,这点我还是不会。

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

手动计算了一遍程序,我感觉很多地方没太看懂。具体问题如下:
1.首先动态规划问题应该是由小算到大。由前面的值推算出后面的值,但是代码中的第二层for循环,却是先算的大(而且这个大还不知道怎么计算。。。)
2.dp的定义没看懂,dp.back()这个函数是什么也不知道。




原文地址:https://www.cnblogs.com/maowuyu-xb/p/6433271.html