動態規划------平均切分數組之和為兩部分


題目描述:

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()這個函數是什么也不知道。




 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM