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

0-1背包問題思路分析,重點(diǎn)解釋一維dp數(shù)組的01背包問題為什么要倒序遍歷背包,以及為什么不能先遍歷背包,只能先遍歷物品

這篇具有很好參考價值的文章主要介紹了0-1背包問題思路分析,重點(diǎn)解釋一維dp數(shù)組的01背包問題為什么要倒序遍歷背包,以及為什么不能先遍歷背包,只能先遍歷物品。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報違法"按鈕提交疑問。


前言

對0-1背包問題的二維dp數(shù)組以及一維dp數(shù)組的思路分析
來源:代碼隨想錄 link

本文是我對01背包問題的理解,在本文中具體分析dp數(shù)組的形成過程,最核心的地方就是我對每種情況下的01背包問題給出了代碼運(yùn)行結(jié)果,便于讀者理解。
重點(diǎn)解釋了為什么一維dp數(shù)組的01背包問題為什么要倒敘遍歷背包,以及為什么不能先遍歷背包,只能先遍歷物品。

1,一維dp數(shù)組為什么不能正序遍歷書包?
因為正序遍歷背包會導(dǎo)致同一個物品被放了兩次,但是01背包要求同一物品只能被放一次。

2,一維dp數(shù)組為什么不能先遍歷背包?
因為能用一維dp數(shù)組解決01背包問題的原理就是先遍歷物品時dp數(shù)組是一行一行形成的(具體過程正文中有結(jié)果顯示),下一行的數(shù)據(jù)只受到上一行數(shù)據(jù)的影響,因此可以只用一維數(shù)組去解決01背包問題,但是先遍歷書包時dp數(shù)組時一列一列形成的(具體過程正文中有結(jié)果顯示),這二者本質(zhì)上就是相悖的,如果要強(qiáng)行理解的話,就是如果先遍歷背包時,只會往背包中放入一件物品(正文中有具體結(jié)果演示)。
文章來源地址http://www.zghlxwxcb.cn/news/detail-775217.html


一、0-1背包問題

有n件物品和一個最多能背重量為w 的背包。第i件物品的重量是weight[i],得到的價值是value[i] 。每件物品只能用一次,求解將哪些物品裝入背包里物品價值總和最大
01背包為什么要倒著循環(huán),動態(tài)規(guī)劃

舉例說明:假設(shè)背包最大重量為4,物品的重量以及價值如表所示:

重量weight 價值value
物品0 1 15
物品1 3 20
物品2 4 30

下面分別通過二維db數(shù)組以及一維dp數(shù)組進(jìn)行分析。

二、二維dp數(shù)組01背包問題代碼詳解

1.遞推關(guān)系式

dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);

dp[i][j]:從物品0到物品i中任意取,放入容量為j的背包,可放入物品的最大價值為dp[i][j]。
遞推關(guān)系式的含義為:01背包問題可以分解為不放物品i和放物品i
即dp[i][j]為放物品i和不放物品i的得到的價值的最大值。

2.代碼詳解

2.1先遍歷物品

根據(jù)代碼隨想錄的介紹,二維dp數(shù)組的01背包問題先遍歷物品還是先遍歷背包均可。
如果先遍歷物品,代碼如下:
其中為了看到每次遍歷后dp數(shù)組的變化,我將其打印輸出,以便分析。

#include<iostream>
#include<vector>
#include <iomanip>
using namespace std;
//打印dp數(shù)組
void print_dparr(vector<vector<int>>& dp)
{
    int row = dp.size();
    int col = dp[0].size();
    for (int i = 0;i < row;i++)
    {
        for (int j = 0;j < col;j++)
        {
            cout <<std::left << setw(2) << dp[i][j] << " ";
        }
        cout << endl;
    }
    cout << endl;
}

void test_2_wei_bag_problem1() {
    vector<int> weight = { 1, 3, 4 };
    vector<int> value = { 15, 20, 30 };
    int bagweight = 4;

    // 二維數(shù)組
    vector<vector<int>> dp(weight.size(), vector<int>(bagweight + 1, 0));

    // 初始化
    for (int j = weight[0]; j <= bagweight; j++) {
        dp[0][j] = value[0];
      
    }
    cout << "從物品0中取物品" << endl;
    print_dparr(dp);
    // weight數(shù)組的大小 就是物品個數(shù)
    for (int i = 1; i < weight.size(); i++) { // 遍歷物品
        for (int j = 0; j <= bagweight; j++) { // 遍歷背包容量
            if (j < weight[i]) dp[i][j] = dp[i - 1][j];
            else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
        }
        cout << "從物品0"<<"到物品"<<i << "中取物品" << endl;
        print_dparr(dp);
    }
    cout <<"最終結(jié)果為:"<< dp[weight.size() - 1][bagweight] << endl;
}
int main() {
    test_2_wei_bag_problem1();
    return 0;
}

代碼運(yùn)行結(jié)果如下:

dp數(shù)組形成過程

01背包為什么要倒著循環(huán),動態(tài)規(guī)劃

先遍歷物品時,整個dp矩陣的下一行數(shù)據(jù)由上一行數(shù)據(jù)計算得到,這也為使用一維dp數(shù)組解決01背包問題埋下伏筆。

2.2.先遍歷背包

同理先遍歷背包再遍歷物品可以得到相同結(jié)果,但是此時由于遍歷順序的改變,dp數(shù)組的形成過程發(fā)生了變化,首先附上代碼:

#include<iostream>
#include<vector>
#include <iomanip>
using namespace std;
//打印dp數(shù)組
void print_dparr(vector<vector<int>>& dp)
{
    int row = dp.size();
    int col = dp[0].size();
  
    for (int i = 0;i < row;i++)
    {
        for (int j = 0;j < col;j++)
        {
            cout <<std::left << setw(2) << dp[i][j] << " ";
        }
        cout << endl;
    }
    cout << endl;
}

void test_2_wei_bag_problem1() {
    vector<int> weight = { 1, 3, 4 };
    vector<int> value = { 15, 20, 30 };
    int bagweight = 4;
    // 二維數(shù)組
    vector<vector<int>> dp(weight.size(), vector<int>(bagweight + 1, 0));
    // 初始化
    for (int j = weight[0]; j <= bagweight; j++) {
        dp[0][j] = value[0];  
    }
    // weight數(shù)組的大小 就是物品個數(shù)
    // 遍歷物品
    int i;
    for (int j = 0; j <= bagweight; j++) { // 遍歷背包容量
        for ( i = 1; i < weight.size(); i++) {
            if (j < weight[i]) dp[i][j] = dp[i - 1][j];
            else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
        }
        cout << "從物品0到物品2中取物品放到容量為"<<j<<"的背包" << endl;
        print_dparr(dp);
    }

    cout <<"最終結(jié)果為:"<< dp[weight.size() - 1][bagweight] << endl;
}

int main() {
    test_2_wei_bag_problem1();
    return 0;
}

代碼運(yùn)行結(jié)果如下:

dp數(shù)組形成過程

01背包為什么要倒著循環(huán),動態(tài)規(guī)劃

dp數(shù)組形成過程分析

由不同的遍歷順序的運(yùn)行結(jié)果可知,先便利物品時dp數(shù)組是一行一行形成的,而先遍歷背包時,dp數(shù)組是一列一列形成的。
如果先遍歷物品,則其思想為從物品0到物品i中選物品放入容量為4的背包中得到的最大價值,其中i由0取到2。
如果先遍歷背包,則其思想為從物品0到物品2中選物品放到容量為j的背包中得到的最大價值,其中j由0取到4。

根據(jù)遞推關(guān)系式可知 dp[i][j] 與 i-1 行的 0 到 j-1 這些數(shù)據(jù)有關(guān),這個思想對理解一維dp數(shù)組為什么倒序遍歷很有用。
二維dp數(shù)組的01背包問題較好理解,下面介紹一維dp數(shù)組的01背包問題。

三、一維dp數(shù)組01背包問題代碼詳解

1.遞推關(guān)系式

利用滾動數(shù)組的形式將二維數(shù)組降為一維數(shù)組,目前考慮先遍歷物品的情況,后續(xù)分析先遍歷背包的情況。

dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

可以寫成一維形式的原因在上述二維dp數(shù)組中其實已經(jīng)略微闡述了一下,現(xiàn)在進(jìn)行詳細(xì)分析。
在上述分析中,我們知道dp[i][j] 只與 i-1 行的 0 到 j-1 這些數(shù)據(jù)有關(guān)即dp矩陣的下一行數(shù)據(jù)只與上一行的一些數(shù)據(jù)有關(guān),因此完全可以利用一維數(shù)組重復(fù)使用。
此時dp[j]的含義為:容量為j的背包可以背的最大價值。

2.代碼詳解

背包倒序遍歷

下面分析一下先遍歷物品時的一維dp數(shù)組解決01背包問題的代碼,注意此時是倒序遍歷背包:

#include<iostream>
#include<vector>
#include <iomanip>
using namespace std;
void print_dparr(vector<int>& dp)
{
    int col = dp.size();
        for (int j = 0;j < col;j++)
        {
            cout << std::left << setw(2) << dp[j] << " ";
        }
    cout << endl;
    cout << endl;
}
void test_1_wei_bag_problem() {
    vector<int> weight = { 1, 3, 4 };
    vector<int> value = { 15, 20, 30 };
    int bagWeight = 4;

    // 初始化
    vector<int> dp(bagWeight + 1, 0);
    for (int i = 0; i < weight.size(); i++) { // 遍歷物品
        for (int j = bagWeight; j >= weight[i]; j--) { // 遍歷背包容量
            dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
        }
        cout << "從物品0到物品" << i << "取物品放到背包中" << endl;
        print_dparr(dp);
    }
    cout <<"最終結(jié)果為:"<< dp[bagWeight] << endl;
}
int main() {
    test_1_wei_bag_problem();
    return 0;
}

運(yùn)行結(jié)果為:

01背包為什么要倒著循環(huán),動態(tài)規(guī)劃

可以看到dp數(shù)組的形成過程與二維dp數(shù)組的形成過程一致,只不過此時的dp數(shù)組是一維的,把三個一維dp數(shù)組拼在一起便形成了二維dp數(shù)組的形式。

背包正序遍歷

在一維dp數(shù)組求解01背包問題中很重要的一點(diǎn)就是書包的遍歷順序,此時書包的遍歷順序只能是倒序遍歷,具體原因如下:

首先先將代碼改成正序遍歷的形式,具體代碼在此不再贅述,只需要把上述代碼的循環(huán)改成如下形式:

  for (int i = 0; i < weight.size(); i++) { // 遍歷物品
        for (int j = 0; j <= bagWeight; j++) {// 遍歷背包容量
            if (j < weight[i]) continue;
            dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
        }
        cout << "從物品0到物品" << i << "取物品放到背包中" << endl;
        print_dparr(dp);
    }

代碼運(yùn)行結(jié)果如下:

01背包為什么要倒著循環(huán),動態(tài)規(guī)劃

可以發(fā)現(xiàn)結(jié)果不一樣了,我們只看第一組數(shù)據(jù)來分析一下產(chǎn)生這種現(xiàn)象的原因,即從物品0中取物品放到背包中得到的結(jié)果為

正序遍歷的結(jié)果:
0 15 30 45 60
倒序遍歷的結(jié)果:
0 15 15 15 15

對比二者可以很容易發(fā)現(xiàn)以下現(xiàn)象:

30=15+15
45=30+15
60=45+15

從遞推關(guān)系式出發(fā):

初始化時dp[0] dp[1] dp[2]均為0,weight[0]=1,value[0]=15。
正序遍歷的結(jié)果
dp[1] = dp[1 - weight[0]] + value[0] = dp[0] + 15 = 15
dp[2] = dp[2 - weight[0]] + value[0] = dp[1] + 15 = 30
倒序遍歷的結(jié)果
dp[2] = dp[2 - weight[0]] + value[0] = dp[1] + 15 = 15
dp[1] = dp[1 - weight[0]] + value[0] = dp[0] + 15 = 15

下面這段話是理解一維dp數(shù)組的核心之處

可以發(fā)現(xiàn)正序遍歷時計算 dp[2] 時把 dp[1]=15 加上去了,意味著物品0被放入了兩次,但實際上加的應(yīng)該是dp[1]=0 。換種方式理解就是dp[2]的正確計算方式應(yīng)該是上一行的dp[1]加上15,而如果正序遍歷的話,便使得dp[1]的值發(fā)生了改變,此時計算的是本行的dp[1]加上15。
因此需要倒序遍歷使得每次取得狀態(tài)不會和之前取得狀態(tài)重合,這樣每種物品就只取一次了。

3.先遍歷背包

上述代碼及分析均基于先遍歷物品,現(xiàn)在分析先遍歷背包時的情況。首先從上述分析中我們可以知道,先遍歷背包時,二維dp數(shù)組是一列一列形成的,但是此時我們用一維dp數(shù)組時,我們只能按行形成dp數(shù)組,所以只能先遍歷物品。

如果非要先遍歷背包,可以將上述代碼中的循環(huán)代碼寫成如下形式:

  for (int j = bagWeight; j >= 0; j--) {
    for (int i = 0; i < weight.size(); i++) { // 遍歷物品
            if (j < weight[i]) continue;
            dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
        }
    	cout << "背包容量為" << j << "時的結(jié)果" << endl;
        print_dparr(dp);
    }

運(yùn)行結(jié)果如下:

01背包為什么要倒著循環(huán),動態(tài)規(guī)劃

首先結(jié)果是錯誤的,這是由于先遍歷背包時我們每次只往背包中放入一件物品,所以在背包容量為4時的結(jié)果為:
0 0 0 0 30

我們只需要知道先遍歷書包跟一維dp數(shù)組解決01背包問題的原理相悖,因為根據(jù)上述的dp數(shù)組形成過程可以知道,先遍歷書包的二維dp數(shù)組是一列一列往后形成的,而一維dp數(shù)組解決01背包問題的原理是由于dp數(shù)組可以按行去生成,這兩者從本質(zhì)上就是相悖的,沒有討論的必要。

總結(jié)

以上就是我對01背包問題的理解,最核心的地方就是我對每種情況下的01背包問題給出來代碼運(yùn)行結(jié)果,便于讀者理解。
對于二維dp數(shù)組的01背包問題不多贅述,比較好理解。重點(diǎn)在于一維dp數(shù)組的01背包問題為什么要倒序遍歷背包,以及為什么不能先遍歷背包,只能先遍歷物品。

1,一維dp數(shù)組為什么不能正序遍歷書包?
因為正序遍歷背包會導(dǎo)致同一個物品被放了兩次,但是01背包要求同一物品只能被放一次。

2,一維dp數(shù)組為什么不能先遍歷背包?
因為能用一維dp數(shù)組解決01背包問題的原理就是先遍歷物品時dp數(shù)組是一行一行形成的(具體過程正文中有結(jié)果顯示),下一行的數(shù)據(jù)只受到上一行數(shù)據(jù)的影響,因此可以只用一維數(shù)組去解決01背包問題,但是先遍歷書包時dp數(shù)組時一列一列形成的(具體過程正文中有結(jié)果顯示),這二者本質(zhì)上就是相悖的,如果要強(qiáng)行理解的話,就是如果先遍歷背包時,只會往背包中放入一件物品(正文中有具體結(jié)果演示)。

到了這里,關(guān)于0-1背包問題思路分析,重點(diǎn)解釋一維dp數(shù)組的01背包問題為什么要倒序遍歷背包,以及為什么不能先遍歷背包,只能先遍歷物品的文章就介紹完了。如果您還想了解更多內(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ī)/事實不符,請點(diǎn)擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實,立即刪除!

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

相關(guān)文章

  • 算法訓(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 對于面試的話,其實掌握01背包,和完全背包,就夠用了,最多可以再來一個多重背包。 而完全背包又是也是01背包稍作變化而來,即:完全

    2024年02月01日
    瀏覽(26)
  • 動態(tài)規(guī)劃DP之背包問題3---多重背包問題

    動態(tài)規(guī)劃DP之背包問題3---多重背包問題

    目錄 DP分析: 優(yōu)化: ?二進(jìn)制優(yōu)化 例題: ? ? ? ? 01背包是每個物品只有一個,完全背包問題是每個物品有無限個。 ? ? ? ? 那么多重背包問題就是 每個物品有有限個 。 有?N?種物品和一個容量是?V?的背包。 第?i?種物品最多有?si?件,每件體積是?vi,價值是?wi。 求解

    2024年03月20日
    瀏覽(43)
  • 【DP】學(xué)習(xí)之背包問題

    【DP】學(xué)習(xí)之背包問題

    2. 01背包問題 - AcWing題庫 記憶化搜索 ?這里補(bǔ)個圖,DFS是自頂向下推的 dp的遞推是從下往上 用一維數(shù)組代替二維數(shù)組? 進(jìn)一步優(yōu)化 這里m從m走到0,的原因是只讓每個物品最多拿一次。 如果正序枚舉體積,就會讓物品被拿多次,從而違反規(guī)則, 但完全背包不用考慮這個問題,

    2024年02月03日
    瀏覽(17)
  • C++ DP算法,動態(tài)規(guī)劃——背包問題(背包九講)

    有N件物品和一個容量為 V V V 的背包。放入第i件物品耗費(fèi)的空間是 C i C_i C i ? ,得到的價值是 W i W_i W i ? 。 求解將哪些物品裝入背包可使價值總和最大。 這是最基礎(chǔ)的背包問題,特點(diǎn)是:每種物品僅有一件,可以選擇放或不放。 用子問題定義狀態(tài):即 F [ i , v ] F[i, v] F

    2024年02月16日
    瀏覽(30)
  • 【算法宇宙——在故事中學(xué)算法】背包dp之完全背包問題

    【算法宇宙——在故事中學(xué)算法】背包dp之完全背包問題

    學(xué)習(xí)者不靈絲相傳,而自杖明月相反,子來此事卻無得失。 盡管計算機(jī)是門嚴(yán)謹(jǐn)?shù)膶W(xué)科,但正因為嚴(yán)謹(jǐn),所以要有趣味才能看得下去。在筆者的前幾篇算法類文章中,都采用了以小故事作為引入的方式來介紹算法,但在回看的時候發(fā)現(xiàn)學(xué)術(shù)味還是太濃了,完全沒有想看下去的

    2023年04月20日
    瀏覽(25)
  • 【LeetCode動態(tài)規(guī)劃#06】分割等和子集(01背包問題一維寫法實戰(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)
  • DP算法應(yīng)用:背包問題解析與實現(xiàn)

    DP算法應(yīng)用:背包問題解析與實現(xiàn)

    通過詳細(xì)解析背包問題的動態(tài)規(guī)劃解法,包括狀態(tài)設(shè)計、狀態(tài)轉(zhuǎn)移方程、兩種編碼方法(自頂向下和自下而上),以及滾動數(shù)組的優(yōu)化,幫助理解和實現(xiàn)動態(tài)規(guī)劃算法。

    2023年04月08日
    瀏覽(19)
  • 【LeetCode動態(tài)規(guī)劃#07】01背包問題一維寫法(狀態(tài)壓縮)實戰(zhàn),其二(目標(biāo)和、零一和)

    【LeetCode動態(tài)規(guī)劃#07】01背包問題一維寫法(狀態(tài)壓縮)實戰(zhàn),其二(目標(biāo)和、零一和)

    力扣題目鏈接(opens new window) 難度:中等 給定一個非負(fù)整數(shù)數(shù)組,a1, a2, ..., an, 和一個目標(biāo)數(shù),S?,F(xiàn)在你有兩個符號 + 和 -。對于數(shù)組中的任意一個整數(shù),你都可以從 + 或 -中選擇一個符號添加在前面。 返回可以使最終數(shù)組和為目標(biāo)數(shù) S 的所有添加符號的方法數(shù)。 示例: 輸入

    2023年04月18日
    瀏覽(87)
  • 算法第十五期——動態(tài)規(guī)劃(DP)之各種背包問題

    目錄 0、背包問題分類 1、?0/1背包簡化版 【代碼】 2、0/ 1背包的方案數(shù) 【思路】

    2023年04月09日
    瀏覽(26)
  • 【Java 動態(tài)數(shù)據(jù)統(tǒng)計圖】動態(tài)數(shù)據(jù)統(tǒng)計思路案例(動態(tài),排序,動態(tài)數(shù)組(重點(diǎn)推薦))七(129)

    【Java 動態(tài)數(shù)據(jù)統(tǒng)計圖】動態(tài)數(shù)據(jù)統(tǒng)計思路案例(動態(tài),排序,動態(tài)數(shù)組(重點(diǎn)推薦))七(129)

    需求 :前端根據(jù)后端的返回數(shù)據(jù):畫統(tǒng)計圖; 說明: 1.X軸為地域,Y軸為地域出現(xiàn)的次數(shù); 2. 動態(tài)展示(有地域展示,沒有不展示,且高低排序) Demo案例 : 測試輸出 : 案例二 : postman接口測試 :

    2024年02月10日
    瀏覽(22)

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

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

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

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

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包