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

【十七】【動態(tài)規(guī)劃】DP41 【模板】01背包、416. 分割等和子集、494. 目標(biāo)和,三道題目深度解析

這篇具有很好參考價值的文章主要介紹了【十七】【動態(tài)規(guī)劃】DP41 【模板】01背包、416. 分割等和子集、494. 目標(biāo)和,三道題目深度解析。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報違法"按鈕提交疑問。

動態(tài)規(guī)劃

動態(tài)規(guī)劃就像是解決問題的一種策略,它可以幫助我們更高效地找到問題的解決方案。這個策略的核心思想就是將問題分解為一系列的小問題,并將每個小問題的解保存起來。這樣,當(dāng)我們需要解決原始問題的時候,我們就可以直接利用已經(jīng)計算好的小問題的解,而不需要重復(fù)計算。

動態(tài)規(guī)劃與數(shù)學(xué)歸納法思想上十分相似。

數(shù)學(xué)歸納法:

  1. 基礎(chǔ)步驟(base case):首先證明命題在最小的基礎(chǔ)情況下成立。通常這是一個較簡單的情況,可以直接驗證命題是否成立。

  2. 歸納步驟(inductive step):假設(shè)命題在某個情況下成立,然后證明在下一個情況下也成立。這個證明可以通過推理推斷出結(jié)論或使用一些已知的規(guī)律來得到。

通過反復(fù)迭代歸納步驟,我們可以推導(dǎo)出命題在所有情況下成立的結(jié)論。

動態(tài)規(guī)劃:

  1. 狀態(tài)表示:

  2. 狀態(tài)轉(zhuǎn)移方程:

  3. 初始化:

  4. 填表順序:

  5. 返回值:

數(shù)學(xué)歸納法的基礎(chǔ)步驟相當(dāng)于動態(tài)規(guī)劃中初始化步驟。

數(shù)學(xué)歸納法的歸納步驟相當(dāng)于動態(tài)規(guī)劃中推導(dǎo)狀態(tài)轉(zhuǎn)移方程。

動態(tài)規(guī)劃的思想和數(shù)學(xué)歸納法思想類似。

在動態(tài)規(guī)劃中,首先得到狀態(tài)在最小的基礎(chǔ)情況下的值,然后通過狀態(tài)轉(zhuǎn)移方程,得到下一個狀態(tài)的值,反復(fù)迭代,最終得到我們期望的狀態(tài)下的值。

接下來我們通過三道例題,深入理解動態(tài)規(guī)劃思想,以及實(shí)現(xiàn)動態(tài)規(guī)劃的具體步驟。

DP41 【模板】01背包

【十七】【動態(tài)規(guī)劃】DP41 【模板】01背包、416. 分割等和子集、494. 目標(biāo)和,三道題目深度解析,動態(tài)規(guī)劃,動態(tài)規(guī)劃,算法,c++,開發(fā)語言

題目解析

【十七】【動態(tài)規(guī)劃】DP41 【模板】01背包、416. 分割等和子集、494. 目標(biāo)和,三道題目深度解析,動態(tài)規(guī)劃,動態(tài)規(guī)劃,算法,c++,開發(fā)語言

第一問

狀態(tài)表示

狀態(tài)表示通常由經(jīng)驗+題目要求得的。

我們最容易想到的狀態(tài)表示是,定義dp[i]表示從前i個物品中挑選,背包所能達(dá)到的最大價值。

動態(tài)規(guī)劃最重要的是希望能夠推導(dǎo)出一個遞推式,使得我們從一個很小的,很容易得到的基礎(chǔ)解,帶入遞推式,反復(fù)推導(dǎo),最后得到我們希望得到的答案。所以我們很容易想到,根據(jù)從前i個物品中挑選,挑選物品的數(shù)量,構(gòu)成我們的每一項,希望能夠推導(dǎo)出遞推式。

如果我們要推導(dǎo)dp[i],我們找不到前面狀態(tài)和i位置狀態(tài)的關(guān)系,我們知道的信息是前面位置狀態(tài)的值,狀態(tài)值表示所能達(dá)到的最大價值,但是我不知道達(dá)到最大價值時,背包的容量,所以我就沒辦法考慮是否選擇第i個物品放入背包,或者不放入背包,如果我們此時還知道對應(yīng)的背包容量,如果還有位置,我們就可以選擇把第i個物品放進(jìn)去,這樣似乎可以推導(dǎo)出狀態(tài)轉(zhuǎn)移方程。所以此時我們還需要表示一個狀態(tài),就是此時背包內(nèi)物品占用的空間是多大。

因此我們可以定義,f[i]表示從前i個物品中挑選,背包所能達(dá)到的最大價值。g[i]表示從從前i個物品中挑選,背包所能達(dá)到的最大價值時,背包占用的體積。

嘗試推導(dǎo)狀態(tài)轉(zhuǎn)移方程,

  1. 如果把第i個物品放入背包, 此時g[i-1]+v[i]<=V , 此時從前i個物品挑選,所能達(dá)到的最大價值,等價于從前i-1個物品挑選,所能達(dá)到的最大價值加上第i個物品的價值,即在f[i]的基礎(chǔ)上,把第i個物品放入背包,此時f[i]=f[i-1]+w[i],g[i]=g[i-1]+v[i]。

  2. 如果不把第i個物品放入背包, 此時g[i-1]+v[i]>V, 此時f[i]=f[i-1],g[i]=g[i-1],這樣寫是對的嗎?乍一看好像沒什么問題,但這樣不對,有可能從前i-1個物品中挑選,達(dá)到的價值不是最大,而是第二大,此時的體積再加上第i個位置的物品的體積比背包總體積小,但是兩者價值加起來比f[i-1]大,這種情況f[i]!=f[i-1]。也就是我們還需要遍歷前面所有狀態(tài),看看是不是(g[j]+v[i]<=V)這種情況,如果是,記錄一下f[j]的值,找到最大的f[j]值,然后和f[i-1]比較,選大的值。這樣狀態(tài)轉(zhuǎn)移方程的推導(dǎo)就變得十分麻煩,我們希望改變狀態(tài)表示,使得狀態(tài)轉(zhuǎn)移方程能夠正常推導(dǎo),并且簡便。

改進(jìn),

上面遇到的問題是,我們不知道,從前i-1物品挑選,價值和第二大的體積花費(fèi),價值和第三大的體積花費(fèi)......如果知道這些狀態(tài),我們就可以直接用。

我們需要的狀態(tài)是上面的體積花費(fèi),因此我們可以以體積為劃分,定義dp[i][j]表示從前i個物品中挑選,總體積不超過j,所有選法中,所能達(dá)到的最大價值。

這樣我們就使得原先只能存儲最大價值的體積g,轉(zhuǎn)變?yōu)榭梢源鎯Σ煌w積花費(fèi)的二維dp。

綜上所述,狀態(tài)表示為,定義dp[i][j]表示從前i個物品中挑選,總體積不超過j,所有選法中,所能達(dá)到的最大價值。

狀態(tài)轉(zhuǎn)移方程

  1. 如果把第i個物品放入背包, dp[i][j]表示從前i個物品中挑選,總體積不超過j,所有選法中,所能達(dá)到的最大價值。此時剩下的體積最多是j-v[i]。

    1. j-v[i]<0, 此時沒辦法把第i個物品放入背包,此時dp[i][j]=0。

    2. j-v[i]=0, 此時把第i個物品放入背包,背包放不下東西了,此時dp[i][j]=w[i]。

    3. j-v[i]>0, 此時把第i個物品放入背包,背包還能放j-v[i]體積的物品,此時dp[i][j]=dp[i-1][j-v[i]]+w[i]。

  2. 如果不把第i個物品放入背包, 此時背包還能放j體積的物品,從前i-1物品中挑選,dp[i][j]=dp[i-1][j],

當(dāng)j-v[i]=0時,dp[i-1][0]=0,此時dp[i][j]=dp[i-1][j-v[i]]+w[i]。

將上述情況合并和簡化,得到狀態(tài)轉(zhuǎn)移方程為,

 
    dp[i][j] = dp[i - 1][j];
    if (j >= v[i])
        dp[i][j] = max(dp[i][j], dp[i - 1][j - v[i]] + w[i]);

初始化

通過狀態(tài)轉(zhuǎn)移方程,我們我們知道在推導(dǎo)(i,j)位置的狀態(tài)時,需要用到(i-1,j)(i-1,j-v[i])的位置的狀態(tài),j-v[i]一定不會越界,可能越界的是i-1,而我們狀態(tài)表示為定義dp[i][j]表示從前i個物品中挑選,總體積不超過j,所有選法中,所能達(dá)到的最大價值。所以我們本質(zhì)上多添加了一列。我們只需要初始第一行即可。

i=0表示不選物品,所以價值都為0,第一行全部初始化為0。

填表順序

通過狀態(tài)轉(zhuǎn)移方程,我們我們知道在推導(dǎo)(i,j)位置的狀態(tài)時,需要用到(i-1,j)(i-1,j-v[i])的位置的狀態(tài),j-v[i]一定不會越界。

  1. 固定i改變j, i的變化需要從小到大,j的變化可以從小到大也可以從大到小。

  2. 固定j改變i, j的變化需要從小到大,因為要用到(i-1,j)位置的狀態(tài),所以i的變化也需要從小到大。

返回值

狀態(tài)表示為定義dp[i][j]表示從前i個物品中挑選,總體積不超過j,所有選法中,所能達(dá)到的最大價值。

結(jié)合題目意思,需要打印dp[n][V]。

這樣我們就解決了第一問。


第二問

【十七】【動態(tài)規(guī)劃】DP41 【模板】01背包、416. 分割等和子集、494. 目標(biāo)和,三道題目深度解析,動態(tài)規(guī)劃,動態(tài)規(guī)劃,算法,c++,開發(fā)語言

狀態(tài)表示

第二問需要求恰好體積為V時,背包所能達(dá)到的最大價值。

在第一問的基礎(chǔ)上,我們很容易定義這樣一個狀態(tài)表示,定義dp[i][j]表示從前i個物品中挑選,總體積恰好為j,所有選法中所能達(dá)到的最大價值。

狀態(tài)轉(zhuǎn)移方程

  1. 如果把第i個物品放入背包, dp[i][j]表示從前i個物品中挑選,總體積恰好為j,所有選法中所能達(dá)到的最大價值。此時剩下的體積恰好是j-v[i]。

    1. j-v[i]<0, 此時沒辦法把第i個物品放入背包,此時dp[i][j]=0。

    2. j-v[i]=0, 此時把第i個物品放入背包,背包放不下東西了,此時dp[i][j]=w[i]。

    3. j-v[i]>0, 此時把第i個物品放入背包,背包還需要放j-v[i]體積的物品,此時dp[i][j]=dp[i-1][j-v[i]]+w[i]。我這樣寫對嗎?此時背包必須放j-v[i]體積的物品,如果前j-1物品挑選不能滿足體積為j-v[i]的情況,此時dp[i][j]=0。如果寫成上面的式子,dp[i][j]=w[i]。因此我們還需要分類討論。

      1. 如果前j-1物品挑選不能滿足體積為j-v[i]的情況, 此時dp[i][j]=0。

      2. 如果前j-1物品挑選能滿足體積為j-v[i]的情況, 此時dp[i][j]=d[i-1][j-v[i]]+w[i]。

  2. 如果不把第i個物品放入背包, 此時背包還需要放j體積的物品,從前i-1物品中挑選,dp[i][j]=dp[i-1][j]。

當(dāng)j-v[i]=0時,dp[i-1][0]=0,此時dp[i][j]=dp[i-1][j-v[i]]+w[i]。

將上述情況進(jìn)行合并和簡化,得到狀態(tài)轉(zhuǎn)移方程,

 
        dp[i][j] = dp[i - 1][j];
        if (j >= v[i] && dp[i - 1][j - v[i]] != -1)
            dp[i][j] = max(dp[i][j], dp[i - 1][j - v[i]] + w[i]);

用-1表示前j-1物品挑選不能滿足體積為j-v[i]的情況。

初始化

通過狀態(tài)轉(zhuǎn)移方程,我們我們知道在推導(dǎo)(i,j)位置的狀態(tài)時,需要用到(i-1,j)(i-1,j-v[i])的位置的狀態(tài),j-v[i]一定不會越界,可能越界的是i-1,而我們狀態(tài)表示為定義dp[i][j]表示從前i個物品中挑選,總體積不超過j,所有選法中,所能達(dá)到的最大價值。所以我們本質(zhì)上多添加了一列。我們只需要初始第一行即可。

i=0,表示不選擇物品,最大價值為0,所以(0,0)為0,其他位置為-1,表示達(dá)不到恰好體積為j的情況。

 
    for (int j = 1; j <= V; j++) dp[0][j] = -1;

填表順序

通過狀態(tài)轉(zhuǎn)移方程,我們我們知道在推導(dǎo)(i,j)位置的狀態(tài)時,需要用到(i-1,j)(i-1,j-v[i])的位置的狀態(tài),j-v[i]一定不會越界。

  1. 固定i改變j, i的變化需要從小到大,j的變化可以從小到大也可以從大到小。

  2. 固定j改變i, j的變化需要從小到大,因為要用到(i-1,j)位置的狀態(tài),所以i的變化也需要從小到大。

返回值

狀態(tài)表示為定義dp[i][j]表示從前i個物品中挑選,總體積不超過j,所有選法中,所能達(dá)到的最大價值。

結(jié)合題目意思,需要打印dp[n][V]。此時需要判斷dp[n][V]是否==-1,等于-1表示不存在,無解返回0,如果不為-1,返回dp[n][V]。

代碼實(shí)現(xiàn)

 
#include <iostream>
#include <string.h>

using namespace std;

const int N = 1010;

int n, V, v[N], w[N];
int dp[N][N];

int main() {

    cin >> n >> V;
    for (int i = 1; i <= n; i++)
        cin >> v[i] >> w[i];


    for (int i = 1; i <= n; i++)
        for (int j = 0; j <= V; j++) { 
            dp[i][j] = dp[i - 1][j];
            if (j >= v[i])
                dp[i][j] = max(dp[i][j], dp[i - 1][j - v[i]] + w[i]);
        }

    cout << dp[n][V] << endl;

//第二問
    memset(dp, 0, sizeof dp);
    for (int j = 1; j <= V; j++) dp[0][j] = -1;
    for (int i = 1; i <= n; i++)
        for (int j = 0; j <= V; j++) { 
            dp[i][j] = dp[i - 1][j];
            if (j >= v[i] && dp[i - 1][j - v[i]] != -1)
                dp[i][j] = max(dp[i][j], dp[i - 1][j - v[i]] + w[i]);
        }
    cout << (dp[n][V] == -1 ? 0 : dp[n][V]) << endl;

    return 0;
}

416. 分割等和子集 - 力扣(LeetCode)

【十七】【動態(tài)規(guī)劃】DP41 【模板】01背包、416. 分割等和子集、494. 目標(biāo)和,三道題目深度解析,動態(tài)規(guī)劃,動態(tài)規(guī)劃,算法,c++,開發(fā)語言

題目解析

【十七】【動態(tài)規(guī)劃】DP41 【模板】01背包、416. 分割等和子集、494. 目標(biāo)和,三道題目深度解析,動態(tài)規(guī)劃,動態(tài)規(guī)劃,算法,c++,開發(fā)語言

狀態(tài)表示

01背包問題本質(zhì)是從一些物品中選物品,本質(zhì)上是有關(guān)組合的問題,而本題目也是在一些數(shù)中挑選出一些數(shù),因為我們可以以01背包為模版,定義狀態(tài)表示。

01背包問題的狀態(tài)表示為,

定義dp[i][j]表示從前i個物品中挑選,總體積不超過j,所有選法中,所能達(dá)到的最大價值。

定義dp[i][j]表示從前i個物品中挑選,總體積為j,所有選法中,所能達(dá)到的最大價值。

因此我們可以定義dp[i][j]表示能否從nums[0,i]區(qū)間中挑選元素,使總數(shù)字和為j。

狀態(tài)轉(zhuǎn)移方程

【十七】【動態(tài)規(guī)劃】DP41 【模板】01背包、416. 分割等和子集、494. 目標(biāo)和,三道題目深度解析,動態(tài)規(guī)劃,動態(tài)規(guī)劃,算法,c++,開發(fā)語言

狀態(tài)轉(zhuǎn)移方程的推導(dǎo)是以最后一個位置的狀況進(jìn)行分類討論。

  1. 如果挑選i位置的元素,

    1. 如果j-nums[i]<0, 此時表示i位置元素比j還要大,此時dp[i][j]=false;

    2. 如果j-nums[i] =0, 此時dp[i][j]=true;

    3. 如果j-nums[i]>0, 此時dp[i][j]=dp[i-1][j-nums[i]];

  2. 如果不挑選i位置的元素, 此時dp[i][j]=dp[i-1][j];

將上述情況進(jìn)行合并和簡化,得到狀態(tài)轉(zhuǎn)移方程,

 
        dp[i][j] = dp[i - 1][j];
        if (j > nums[i])
            dp[i][j] = dp[i][j] || dp[i - 1][j - nums[i]];
        else if(j == nums[i]) dp[i][j] = true;

初始化

【十七】【動態(tài)規(guī)劃】DP41 【模板】01背包、416. 分割等和子集、494. 目標(biāo)和,三道題目深度解析,動態(tài)規(guī)劃,動態(tài)規(guī)劃,算法,c++,開發(fā)語言

根據(jù)狀態(tài)轉(zhuǎn)移方程,我們知道,在推導(dǎo)(i,j)位置的狀態(tài)時,需要用到(i-1,j)(i-1,j-nums[i])位置的狀態(tài)。

所以我們需要初始化第一行位置的狀態(tài),在推導(dǎo)藍(lán)色部分位置狀態(tài)的時候沒有前驅(qū),所以需要初始化這些位置的狀態(tài)值。使用(i-1,j-nums[i])狀態(tài)時,j-nums[i]一定不會越界。所以只需要考慮(i-1,j)

狀態(tài)表示,定義dp[i][j]表示能否從nums[0,i]區(qū)間中挑選元素,使總數(shù)字和為j。

i==0,表示從只選擇挑選或者不挑選第一個元素,能否使數(shù)字和為j。如果nums[0]==j,或者j==0,此時dp[i][j]=true。

故初始化為,

 
        for (int j = 0; j < n; j++)
            if(nums[0] == j || j == 0) dp[0][j] = true;

填表順序

根據(jù)狀態(tài)轉(zhuǎn)移方程,我們知道,在推導(dǎo)(i,j)位置的狀態(tài)時,需要用到(i-1,j)(i-1,j-nums[i])位置的狀態(tài)。使用(i-1,j-nums[i])狀態(tài)時,j-nums[i]一定不會越界。所以只需要考慮(i-1,j)

  1. 固定i改變j, i的變化,需要從小到大,j的變化可以從小到大也可以從大到小。

  2. 固定j改變i, j的變化可以從小到大也可以從大到小,i的變化需要從小到大。

返回值

狀態(tài)表示,定義dp[i][j]表示能否從nums[0,i]區(qū)間中挑選元素,使總數(shù)字和為j。

結(jié)合題目意思,我們需要在nums[0,n-1]區(qū)間挑選元素,使總數(shù)字和為所有元素數(shù)字和的一半。

所以我們可以用aim表示所有元素數(shù)字和的一半,

因此需要返回dp[n-1][aim];

代碼實(shí)現(xiàn)

 
class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int n = nums.size(), sum = 0;
        for (auto x : nums)
            sum += x;
        if (sum % 2)
            return false; // 如果不能平分,直接返回 false
        int aim = sum / 2;
        vector<vector<bool>> dp(n, vector<bool>(aim + 1));
        for (int j = 0; j < n; j++)
            if(nums[0] == j || j==0) dp[0][j] = true;
        for (int i = 1; i < n; i++)
            for (int j = 0; j <= aim; j++) {
                dp[i][j] = dp[i - 1][j];
                if (j > nums[i])
                    dp[i][j] = dp[i][j] || dp[i - 1][j - nums[i]];
                else if (j == nums[i])
                    dp[i][j] = true;
            }
        return dp[n - 1][aim];
    }
};

494. 目標(biāo)和 - 力扣(LeetCode)

【十七】【動態(tài)規(guī)劃】DP41 【模板】01背包、416. 分割等和子集、494. 目標(biāo)和,三道題目深度解析,動態(tài)規(guī)劃,動態(tài)規(guī)劃,算法,c++,開發(fā)語言

題目解析

【十七】【動態(tài)規(guī)劃】DP41 【模板】01背包、416. 分割等和子集、494. 目標(biāo)和,三道題目深度解析,動態(tài)規(guī)劃,動態(tài)規(guī)劃,算法,c++,開發(fā)語言

狀態(tài)表示

01背包問題本質(zhì)是從一些物品中選物品,本質(zhì)上是有關(guān)組合的問題,而本題目也是在一些數(shù)中挑選出一些數(shù),因為我們可以以01背包為模版,定義狀態(tài)表示。

01背包問題的狀態(tài)表示為,

定義dp[i][j]表示從前i個物品中挑選,總體積不超過j,所有選法中,所能達(dá)到的最大價值。

定義dp[i][j]表示從前i個物品中挑選,總體積為j,所有選法中,所能達(dá)到的最大價值。

因此我們可以定義dp[i][j]表示從nums[0,i]區(qū)間中挑選元素,使總數(shù)字和為j,一共有多少種選法。

狀態(tài)轉(zhuǎn)移方程

【十七】【動態(tài)規(guī)劃】DP41 【模板】01背包、416. 分割等和子集、494. 目標(biāo)和,三道題目深度解析,動態(tài)規(guī)劃,動態(tài)規(guī)劃,算法,c++,開發(fā)語言

狀態(tài)轉(zhuǎn)移方程的推導(dǎo)是以最后一個位置的狀況進(jìn)行分類討論。

dp[i][j]表示從nums[0,i]區(qū)間中挑選元素,使總數(shù)字和為j,一共有多少種選法。

  1. 如果挑選i位置的元素,

    1. 如果j-nums[i]<0, 此時表示i位置元素比j還要大,此時dp[i][j]=0;

    2. 如果j-nums[i] =0, 此時表示挑選i位置元素后,數(shù)字和就為j了,此時還需要考慮數(shù)字和為0的挑選種類次數(shù),所以此時dp[i][j]=dp[i-1][j-nums[i-1]];

    3. 如果j-nums[i]>0, dp[i-1][j-nums[i]]表示從nums[0,i-1]區(qū)間中挑選元素,使總數(shù)字和為j-nums[i],一共有多少種選法。這些選法的每一種選法中,都加上i位置的元素,選法數(shù)不變,但是這些選法使得數(shù)字和都為j。 此時dp[i][j]=dp[i-1][j-nums[i]];

  2. 如果不挑選i位置的元素, 此時dp[i][j]=dp[i-1][j];

將上述情況進(jìn)行合并和簡化,得到狀態(tài)轉(zhuǎn)移方程,

 
                dp[i][j] = dp[i - 1][j];
                if (j >= nums[i])
                    dp[i][j] += dp[i - 1][j - nums[i]];
            

初始化

【十七】【動態(tài)規(guī)劃】DP41 【模板】01背包、416. 分割等和子集、494. 目標(biāo)和,三道題目深度解析,動態(tài)規(guī)劃,動態(tài)規(guī)劃,算法,c++,開發(fā)語言

根據(jù)狀態(tài)轉(zhuǎn)移方程,我們知道,在推導(dǎo)(i,j)位置的狀態(tài)時,需要用到(i-1,j)(i-1,j-nums[i])位置的狀態(tài),而只有j>nums[i]的時候才會用到(i-1,j-nums[i])位置的狀態(tài),所以我們只需要考慮(i-1,j)。

所以我們需要初始化第一行位置的狀態(tài),在推導(dǎo)藍(lán)色部分位置狀態(tài)的時候沒有前驅(qū),所以需要初始化這些位置的狀態(tài)值。

我們可以添加虛擬節(jié)點(diǎn),即多添加一行和一列,使這些虛擬節(jié)點(diǎn)成為藍(lán)色位置的前驅(qū),這樣就不用初始化藍(lán)色位置的值,而變?yōu)槌跏蓟摂M節(jié)點(diǎn)即可。

這樣做的好處是,虛擬結(jié)點(diǎn)的初始化可能比藍(lán)色部分位置狀態(tài)的初始化要簡便許多。

【十七】【動態(tài)規(guī)劃】DP41 【模板】01背包、416. 分割等和子集、494. 目標(biāo)和,三道題目深度解析,動態(tài)規(guī)劃,動態(tài)規(guī)劃,算法,c++,開發(fā)語言

添加虛擬結(jié)點(diǎn)后,狀態(tài)表示和狀態(tài)轉(zhuǎn)移方程會發(fā)生改變,即,

dp[i][j]表示從nums[0,i-1]區(qū)間中挑選元素,使總數(shù)字和為j,一共有多少種選法。

 
                dp[i][j] = dp[i - 1][j];
                if (j >= nums[i - 1])
                    dp[i][j] += dp[i - 1][j - nums[i - 1]];

添加虛擬節(jié)點(diǎn)后,有兩點(diǎn)注意事項,

  1. 初始化虛擬節(jié)點(diǎn),必須保證推導(dǎo)后續(xù)位置的狀態(tài)的正確性。

  2. 下標(biāo)的映射關(guān)系。

初始化虛擬節(jié)點(diǎn):

我們根據(jù)狀態(tài)表示,dp[i][j]表示從nums[0,i-1]區(qū)間中挑選元素,使總數(shù)字和為j,一共有多少種選法。我們同時可以根據(jù)狀態(tài)表示賦予第一行意義,表示不選元素時的情況。

接下來初始化綠色位置的狀態(tài)。

此時表示不選擇元素,此時(0,0)位置dp[0][0]=1,其他位置為0。

下標(biāo)映射關(guān)系:

  1. 此時,dp[i][j]表示從nums[0,i-1]區(qū)間中挑選元素,使總數(shù)字和為j,一共有多少種選法。 dp中i對應(yīng)nums的i-1。

  2. 如果在nums前面添加一個占位符,就可以使得dp中i,j繼續(xù)映射nums1,nums2中i,j。

我們這里選擇第一種解決辦法。

得到初始化,

 
        dp[0][0] = 1;  

填表順序

根據(jù)狀態(tài)轉(zhuǎn)移方程,我們知道,在推導(dǎo)(i,j)位置的狀態(tài)時,需要用到(i-1,j)(i-1,j-nums[i])位置的狀態(tài)。使用(i-1,j-nums[i])狀態(tài)時,j-nums[i]一定不會越界。所以只需要考慮(i-1,j)

  1. 固定i改變j, i的變化,需要從小到大,j的變化可以從小到大也可以從大到小。

  2. 固定j改變i, j的變化可以從小到大也可以從大到小,i的變化需要從小到大。

返回值

dp[i][j]表示從nums[0,i-1]區(qū)間中挑選元素,使總數(shù)字和為j,一共有多少種選法。

結(jié)合題目意思,我們需要在nums[0,n-1]區(qū)間挑選元素,使總數(shù)字和為所有元素數(shù)字和的一半。

所以我們可以用aim表示所有元素數(shù)字和的一半,

因此需要返回dp[n][aim];

代碼實(shí)現(xiàn)

 
class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int sum = 0;
        for (auto x : nums)
            sum += x;
        int aim = (sum + target) / 2;

        if (aim < 0 || (sum + target) % 2)
            return 0;
        int n = nums.size();
        vector<vector<int>> dp(n + 1, vector<int>(aim + 1)); // 建表
        dp[0][0] = 1;                                        // 初始化
        for (int i = 1; i <= n; i++)                         // 填表
            for (int j = 0; j <= aim; j++) {
                dp[i][j] = dp[i - 1][j];
                if (j >= nums[i - 1])
                    dp[i][j] += dp[i - 1][j - nums[i - 1]];
            }
        // 返回結(jié)果
        return dp[n][aim];
    }
};

結(jié)尾

今天我們學(xué)習(xí)了動態(tài)規(guī)劃的思想,動態(tài)規(guī)劃思想和數(shù)學(xué)歸納法思想有一些類似,動態(tài)規(guī)劃在模擬數(shù)學(xué)歸納法的過程,已知一個最簡單的基礎(chǔ)解,通過得到前項與后項的推導(dǎo)關(guān)系,由這個最簡單的基礎(chǔ)解,我們可以一步一步推導(dǎo)出我們希望得到的那個解,把我們得到的解依次存放在dp數(shù)組中,dp數(shù)組中對應(yīng)的狀態(tài),就像是數(shù)列里面的每一項。最后感謝您閱讀我的文章,對于動態(tài)規(guī)劃系列,我會一直更新,如果您覺得內(nèi)容有幫助,可以點(diǎn)贊加關(guān)注,以快速閱讀最新文章。

最后,感謝您閱讀我的文章,希望這些內(nèi)容能夠?qū)δ兴鶈l(fā)和幫助。如果您有任何問題或想要分享您的觀點(diǎn),請隨時在評論區(qū)留言。

同時,不要忘記訂閱我的博客以獲取更多有趣的內(nèi)容。在未來的文章中,我將繼續(xù)探討這個話題的不同方面,為您呈現(xiàn)更多深度和見解。

謝謝您的支持,期待與您在下一篇文章中再次相遇!文章來源地址http://www.zghlxwxcb.cn/news/detail-779096.html

到了這里,關(guān)于【十七】【動態(tài)規(guī)劃】DP41 【模板】01背包、416. 分割等和子集、494. 目標(biāo)和,三道題目深度解析的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

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

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

相關(guān)文章

  • 從01背包開始動態(tài)規(guī)劃:暴力解法 + dp + 滾動數(shù)組 + dp優(yōu)化

    從01背包開始動態(tài)規(guī)劃:暴力解法 + dp + 滾動數(shù)組 + dp優(yōu)化

    ? ? 01背包問題是動態(tài)規(guī)劃中最經(jīng)典的問題之一,本篇將通過給出其四種解法,使讀者更深刻理解動態(tài)規(guī)劃。 ? 有N件物品和一個容量是?V 的背包,每個物品有各自的體積和價值,且每個物品只能放一次(這也是01背包名字的由來),如何讓背包里裝入的物品具有最大的價值總

    2024年04月17日
    瀏覽(22)
  • 動態(tài)規(guī)劃(DP)---- 01背包入門詳解----二維圖是學(xué)會的關(guān)鍵

    ? ? 動態(tài)規(guī)劃,Dynamic Programing(簡稱DP),個人認(rèn)為是一種 算法思想 , 用來解決多階段多層次的選擇問題,把一個復(fù)雜的問題分解成每個小塊的子問題然后一個個解決來找到最優(yōu)解。 ? ? DP適用 重疊子問題 和 最優(yōu)子結(jié)構(gòu)的性質(zhì) 的問題。 ? ? DP問題范圍分為 線性與非線性

    2024年02月03日
    瀏覽(19)
  • 算法訓(xùn)練第四十二天|01背包問題 二維 、01背包問題 一維、416. 分割等和子集

    算法訓(xùn)練第四十二天|01背包問題 二維 、01背包問題 一維、416. 分割等和子集

    視頻鏈接:https://www.bilibili.com/video/BV1cg411g7Y6/ 參考:https://programmercarl.com/%E8%83%8C%E5%8C%85%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%8001%E8%83%8C%E5%8C%85-1.html 對于面試的話,其實(shí)掌握01背包,和完全背包,就夠用了,最多可以再來一個多重背包。 而完全背包又是也是01背包稍作變化而來,即:完全

    2024年02月01日
    瀏覽(26)
  • 【LeetCode動態(tài)規(guī)劃#06】分割等和子集(01背包問題一維寫法實(shí)戰(zhàn))

    分割等和子集 給你一個 只包含正整數(shù) 的 非空 數(shù)組 nums 。請你判斷是否可以將這個數(shù)組分割成兩個子集,使得兩個子集的元素和相等。 示例 1: 輸入:nums = [1,5,11,5] 輸出:true 解釋:數(shù)組可以分割成 [1, 5, 5] 和 [11] 示例 2: 輸入:nums = [1,2,3,5] 輸出:false 解釋:數(shù)組不能分割

    2023年04月09日
    瀏覽(121)
  • 01背包問題-遞推公式的自我理解與LeetCode 416. 分割等和子集

    01背包問題-遞推公式的自我理解與LeetCode 416. 分割等和子集

    學(xué)算法好痛苦,完全是對我智力的一次次折磨,看了好多博客,對二維dp數(shù)組的理解都是直接搬了代碼隨想錄,搬了隨想錄又沒詳細(xì)解釋,大家都是一眼看懂的嗎,好吧() 如果有一個容量為 j 的這樣的背包——一個獨(dú)立的,容量為j的背包。(沒把它理解為“剩余容量”) 對于

    2024年02月07日
    瀏覽(18)
  • 力扣算法刷題Day42|動態(tài)規(guī)劃:01背包問題 分割等和子集

    力扣題目:01背包問題(二維數(shù)組) 刷題時長:參考題解 解題方法:動態(tài)規(guī)劃 +?二維dp數(shù)組 復(fù)雜度分析 時間 空間 問題總結(jié) 理解遞推公式困難 本題收獲 動規(guī)思路:兩層for循環(huán),第一層i遍歷物品,第二層j枚舉背包容量以內(nèi)所有值 確定dp數(shù)組及下標(biāo)的含義:dp[i][j] 表示從下標(biāo)

    2024年02月13日
    瀏覽(94)
  • 【力扣】416. 分割等和子集 <動態(tài)規(guī)劃、回溯>

    【力扣】416. 分割等和子集 <動態(tài)規(guī)劃、回溯>

    給你一個 只包含正整數(shù)的非空數(shù)組 nums 。請你判斷是否可以將這個數(shù)組分割成兩個子集,使得兩個子集的元素和相等。 示例 1: 輸入:nums = [1,5,11,5] 輸出:true 解釋:數(shù)組可以分割成 [1, 5, 5] 和 [11] 。 示例 2: 輸入:nums = [1,2,3,5] 輸出:false 解釋:數(shù)組不能分割成兩個元素和

    2024年02月10日
    瀏覽(18)
  • leetcode416. 分割等和子集(動態(tài)規(guī)劃-java)

    leetcode416. 分割等和子集(動態(tài)規(guī)劃-java)

    來源:力扣(LeetCode) 鏈接:https://leetcode.cn/problems/partition-equal-subset-sum 給你一個 只包含正整數(shù) 的 非空 數(shù)組 nums 。請你判斷是否可以將這個數(shù)組分割成兩個子集,使得兩個子集的元素和相等。 示例 1: 輸入:nums = [1,5,11,5] 輸出:true 解釋:數(shù)組可以分割成 [1, 5, 5] 和 [11] 。

    2024年02月11日
    瀏覽(20)
  • 力扣hot100:416.分割等和子集(組合/動態(tài)規(guī)劃/STL問題)

    力扣hot100:416.分割等和子集(組合/動態(tài)規(guī)劃/STL問題)

    組合數(shù)問題 我們思考一下,如果要把數(shù)組分割成兩個子集,并且兩個子集的元素和相等,是否等價于在數(shù)組中尋找若干個數(shù)使之和等于所有數(shù)的一半?是的! 因此我們可以想到,兩種方式: ①回溯的方式找到target,但是回溯是階乘級別的算法,這里會超時。 ②從前往后遍歷

    2024年04月28日
    瀏覽(16)
  • 動態(tài)規(guī)劃(DP)---背包二維圖

    狀態(tài)方程:dp[i][j] = max(dp[i - 1][j],dp[i - 1][j - w[i]] + v[i]) 應(yīng)該是看完我寫的DP文章來的吧,如果沒有看到,希望看看DP那個文章結(jié)合這個理解,DP那個文章內(nèi)部寫了我對于01背包類型的想法與思路,有時間的網(wǎng)友可以了解hhh。 分析這個東東的時候,其實(shí)是四個方向嘛,我推薦要是

    2024年02月03日
    瀏覽(22)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

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

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包