国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

動(dòng)態(tài)規(guī)劃(分割等和子集)

這篇具有很好參考價(jià)值的文章主要介紹了動(dòng)態(tài)規(guī)劃(分割等和子集)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

416. 分割等和子集

題目難易:中等

給定一個(gè)只包含正整數(shù)的非空數(shù)組。是否可以將這個(gè)數(shù)組分割成兩個(gè)子集,使得兩個(gè)子集的元素和相等。

注意: 每個(gè)數(shù)組中的元素不會(huì)超過(guò) 100 數(shù)組的大小不會(huì)超過(guò) 200

示例 1:

輸入: [1, 5, 11, 5]
輸出: true
解釋: 數(shù)組可以分割成 [1, 5, 5] 和 [11].
示例 2:

輸入: [1, 2, 3, 5]
輸出: false
解釋: 數(shù)組不能分割成兩個(gè)元素和相等的子集.
提示:

1 <= nums.length <= 200
1 <= nums[i] <= 100

思路

這道題目初步看,和如下兩題幾乎是一樣的,大家可以用回溯法,解決如下兩題

  • 698.劃分為k個(gè)相等的子集
  • 473.火柴拼正方形

這道題目是要找是否可以將這個(gè)數(shù)組分割成兩個(gè)子集,使得兩個(gè)子集的元素和相等。

那么只要找到集合里能夠出現(xiàn) sum / 2 的子集總和,就算是可以分割成兩個(gè)相同元素和子集了。

本題是可以用回溯暴力搜索出所有答案的,但最后超時(shí)了,也不想再優(yōu)化了,放棄回溯,直接上01背包吧。

如果對(duì)01背包不夠了解,建議仔細(xì)看完如下兩篇:

動(dòng)態(tài)規(guī)劃:關(guān)于01背包問(wèn)題,你該了解這些!(opens new window)
動(dòng)態(tài)規(guī)劃:關(guān)于01背包問(wèn)題,你該了解這些?。L動(dòng)數(shù)組)(opens new window)
#01背包問(wèn)題
背包問(wèn)題,大家都知道,有N件物品和一個(gè)最多能背重量為W 的背包。第i件物品的重量是weight[i],得到的價(jià)值是value[i] 。每件物品只能用一次,求解將哪些物品裝入背包里物品價(jià)值總和最大。

背包問(wèn)題有多種背包方式,常見(jiàn)的有:01背包、完全背包、多重背包、分組背包和混合背包等等。

要注意題目描述中商品是不是可以重復(fù)放入。

即一個(gè)商品如果可以重復(fù)多次放入是完全背包,而只能放入一次是01背包,寫(xiě)法還是不一樣的。

要明確本題中我們要使用的是01背包,因?yàn)樵匚覀冎荒苡靡淮巍?/p>

回歸主題:首先,本題要求集合里能否出現(xiàn)總和為 sum / 2 的子集。

那么來(lái)一一對(duì)應(yīng)一下本題,看看背包問(wèn)題如何來(lái)解決。

只有確定了如下四點(diǎn),才能把01背包問(wèn)題套到本題上來(lái)。

背包的體積為sum / 2
背包要放入的商品(集合里的元素)重量為 元素的數(shù)值,價(jià)值也為元素的數(shù)值
背包如果正好裝滿(mǎn),說(shuō)明找到了總和為 sum / 2 的子集。
背包中每一個(gè)元素是不可重復(fù)放入。
以上分析完,我們就可以套用01背包,來(lái)解決這個(gè)問(wèn)題了。

動(dòng)規(guī)五部曲分析如下:

確定dp數(shù)組以及下標(biāo)的含義
01背包中,dp[j] 表示: 容量為j的背包,所背的物品價(jià)值最大可以為dp[j]。

本題中每一個(gè)元素的數(shù)值既是重量,也是價(jià)值。

套到本題,dp[j]表示 背包總?cè)萘浚ㄋ苎b的總重量)是j,放進(jìn)物品后,背的最大重量為dp[j]。

那么如果背包容量為target, dp[target]就是裝滿(mǎn) 背包之后的重量,所以 當(dāng) dp[target] == target 的時(shí)候,背包就裝滿(mǎn)了。

有錄友可能想,那還有裝不滿(mǎn)的時(shí)候?

拿輸入數(shù)組 [1, 5, 11, 5],舉例, dp[7] 只能等于 6,因?yàn)?只能放進(jìn) 1 和 5。

而dp[6] 就可以等于6了,放進(jìn)1 和 5,那么dp[6] == 6,說(shuō)明背包裝滿(mǎn)了。

確定遞推公式
01背包的遞推公式為:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

本題,相當(dāng)于背包里放入數(shù)值,那么物品i的重量是nums[i],其價(jià)值也是nums[i]。

所以遞推公式:dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);

dp數(shù)組如何初始化
在01背包,一維dp如何初始化,已經(jīng)講過(guò),

從dp[j]的定義來(lái)看,首先dp[0]一定是0。

如果題目給的價(jià)值都是正整數(shù)那么非0下標(biāo)都初始化為0就可以了,如果題目給的價(jià)值有負(fù)數(shù),那么非0下標(biāo)就要初始化為負(fù)無(wú)窮。

這樣才能讓dp數(shù)組在遞推的過(guò)程中取得最大的價(jià)值,而不是被初始值覆蓋了。

本題題目中 只包含正整數(shù)的非空數(shù)組,所以非0下標(biāo)的元素初始化為0就可以了。

代碼如下:

// 題目中說(shuō):每個(gè)數(shù)組中的元素不會(huì)超過(guò) 100,數(shù)組的大小不會(huì)超過(guò) 200
// 總和不會(huì)大于20000,背包最大只需要其中一半,所以10001大小就可以了
vector dp(10001, 0);
確定遍歷順序
在動(dòng)態(tài)規(guī)劃:關(guān)于01背包問(wèn)題,你該了解這些?。L動(dòng)數(shù)組) (opens new window)中就已經(jīng)說(shuō)明:如果使用一維dp數(shù)組,物品遍歷的for循環(huán)放在外層,遍歷背包的for循環(huán)放在內(nèi)層,且內(nèi)層for循環(huán)倒序遍歷!

代碼如下:

// 開(kāi)始 01背包

for(int i = 0; i < nums.size(); i++) {
    for(int j = target; j >= nums[i]; j--) { // 每一個(gè)元素一定是不可重復(fù)放入,所以從大到小遍歷
        dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);
    }
}

舉例推導(dǎo)dp數(shù)組
dp[j]的數(shù)值一定是小于等于j的。

如果dp[j] == j 說(shuō)明,集合中的子集總和正好可以湊成總和j,理解這一點(diǎn)很重要。

用例1,輸入[1,5,11,5] 為例,如圖:
動(dòng)態(tài)規(guī)劃(分割等和子集),leetcode,動(dòng)態(tài)規(guī)劃,算法,leetcode
最后dp[11] == 11,說(shuō)明可以將這個(gè)數(shù)組分割成兩個(gè)子集,使得兩個(gè)子集的元素和相等。

綜上分析完畢,C++代碼如下:

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int sum = 0;

        // dp[i]中的i表示背包內(nèi)總和
        // 題目中說(shuō):每個(gè)數(shù)組中的元素不會(huì)超過(guò) 100,數(shù)組的大小不會(huì)超過(guò) 200
        // 總和不會(huì)大于20000,背包最大只需要其中一半,所以10001大小就可以了
        vector<int> dp(10001, 0);
        for (int i = 0; i < nums.size(); i++) {
            sum += nums[i];
        }
        // 也可以使用庫(kù)函數(shù)一步求和
        // int sum = accumulate(nums.begin(), nums.end(), 0);
        if (sum % 2 == 1) return false;
        int target = sum / 2;

        // 開(kāi)始 01背包
        for(int i = 0; i < nums.size(); i++) {
            for(int j = target; j >= nums[i]; j--) { // 每一個(gè)元素一定是不可重復(fù)放入,所以從大到小遍歷
                dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);
            }
        }
        // 集合中的元素正好可以湊成總和target
        if (dp[target] == target) return true;
        return false;
    }
};

python

class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        _sum = 0

        # dp[i]中的i表示背包內(nèi)總和
        # 題目中說(shuō):每個(gè)數(shù)組中的元素不會(huì)超過(guò) 100,數(shù)組的大小不會(huì)超過(guò) 200
        # 總和不會(huì)大于20000,背包最大只需要其中一半,所以10001大小就可以了
        dp = [0] * 10001
        for num in nums:
            _sum += num
        # 也可以使用內(nèi)置函數(shù)一步求和
        # _sum = sum(nums)
        if _sum % 2 == 1:
            return False
        target = _sum // 2

        # 開(kāi)始 0-1背包
        for num in nums:
            for j in range(target, num - 1, -1):  # 每一個(gè)元素一定是不可重復(fù)放入,所以從大到小遍歷
                dp[j] = max(dp[j], dp[j - num] + num)

        # 集合中的元素正好可以湊成總和target
        if dp[target] == target:
            return True
        return False

(簡(jiǎn)化版)

class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        if sum(nums) % 2 != 0:
            return False
        target = sum(nums) // 2
        dp = [0] * (target + 1)
        for num in nums:
            for j in range(target, num-1, -1):
                dp[j] = max(dp[j], dp[j-num] + num)
        return dp[-1] == target

二維DP版

class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        
        total_sum = sum(nums)

        if total_sum % 2 != 0:
            return False

        target_sum = total_sum // 2
        dp = [[False] * (target_sum + 1) for _ in range(len(nums) + 1)]

        # 初始化第一行(空子集可以得到和為0)
        for i in range(len(nums) + 1):
            dp[i][0] = True

        for i in range(1, len(nums) + 1):
            for j in range(1, target_sum + 1):
                if j < nums[i - 1]:
                    # 當(dāng)前數(shù)字大于目標(biāo)和時(shí),無(wú)法使用該數(shù)字
                    dp[i][j] = dp[i - 1][j]
                else:
                    # 當(dāng)前數(shù)字小于等于目標(biāo)和時(shí),可以選擇使用或不使用該數(shù)字
                    dp[i][j] = dp[i - 1][j] or dp[i - 1][j - nums[i - 1]]

        return dp[len(nums)][target_sum]

一維DP版文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-785254.html

class Solution:
    def canPartition(self, nums: List[int]) -> bool:

        total_sum = sum(nums)

        if total_sum % 2 != 0:
            return False

        target_sum = total_sum // 2
        dp = [False] * (target_sum + 1)
        dp[0] = True

        for num in nums:
            # 從target_sum逆序迭代到num,步長(zhǎng)為-1
            for i in range(target_sum, num - 1, -1):
                dp[i] = dp[i] or dp[i - num]
        return dp[target_sum]

到了這里,關(guān)于動(dòng)態(tài)規(guī)劃(分割等和子集)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶(hù)投稿,該文觀(guān)點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包