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

背包問題分析代碼詳解【01背包+完全背包+多重背包】

這篇具有很好參考價值的文章主要介紹了背包問題分析代碼詳解【01背包+完全背包+多重背包】。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

一、01背包問題

問題描述:

有 N 件物品和一個容量為 V 的背包,每件物品有各自的價值且只能被選擇一次,要求在有限的背包容量下,裝入的物品總價值最大。

樸素01背包
  1. 狀態(tài)f[i , j]定義:在前i個物品中選,總體積不超過j的價值最大值
  2. 狀態(tài)轉(zhuǎn)移
    1)選第i個物品:f[i,j] = f[i-1,j]
    2)不選第i個物品:f[i,j] = f[i-1, j-v[i] ] +w[i]
    我們的決策是如何取到最大價值,因此以上兩種情況取max( )
for(int i = 1; i <= n; i++) 
        for(int j = 0; j <= m; j++)
        {
            //  當前背包容量裝不進第i個物品,則價值等于前i-1個物品
                f[i][j] = f[i - 1][j];
            // 能裝,需進行決策是否選擇第i個物品
            if(j>=v[i])    
                f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]);
        }           
01背包優(yōu)化成一維

一維01背包就是將狀態(tài)f[i,j] 優(yōu)化到一維f[j] ,減低了內(nèi)存的開銷,使代碼更簡潔。

這種方法適合任何背包問題,在接下來的所有背包問題我們都是用一維的方法來分析。

1. 為什么可以這樣變形呢?

? 從樸素01背包問題來看,我們最終想要的答案是f[n,m] ,跟前n-1層無關(guān),只是在計算的時候才會用到,但是用到的并不是所有的數(shù)據(jù),我們通過狀態(tài)轉(zhuǎn)移可以發(fā)現(xiàn)我們當前層的轉(zhuǎn)移只用到了上一層的數(shù)據(jù),因此我們只需要保存上一層的數(shù)據(jù),每次用上一層的數(shù)據(jù)計算即可。

2. 在二維變?yōu)橐痪S的時候,有什么需要注意?

1) 在枚舉背包容量的時候需要逆序

? 因為在計算的我們是用較小的體積去更新較大的體積,所有我們每次在更新的時候,用到的是本層的狀態(tài),而不是上一層的狀態(tài)。

2) 如何由二維優(yōu)化到一維

我們只需要將前面一維刪掉即可,具體變化如下:

      for(int i = 1; i <= n; i++) 
              for(int j = 1; j <= m; j++)
              {
                  //  當前背包容量裝不進第i個物品,則價值等于前i-1個物品
                      f[i][j] = f[i - 1][j];  	//優(yōu)化前
                      f[j] = f[j]      			//優(yōu)化后
           
                  // 能裝,需進行決策是否選擇第i個物品
                  if(j>=v[i])    
                      f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]);  //優(yōu)化前
                      f[j] = max(f[j],f[j - v[i]]+w[i]);					//優(yōu)化后
              }    

接下來我們調(diào)整上述代碼中出現(xiàn)的一些冗余代碼,具體如下

  • 由于f[j] = f[j] 是恒等式,無意義,所有我們?nèi)サ簟?/p>

  • 我們將if(j>=v[i])放到循環(huán)條件里面。

這步為什么成立?變成一維之后,在j<v[i]的時候,由于我們用到的數(shù)據(jù)就是上一層的數(shù)據(jù),所以我們不需要改變當前f的值。只有當j>=v[i]的時候,我們才拿上一層的值和放當前物品的價值取max即可。

簡言之,在j<v[i]的時候,不需要動。在j>=v[i]的時候,令f[j] = max(f[j],f[j - v[i]]+w[i])

所以我們只需要從>=v[i]開始枚舉

最終代碼:

for(int i = 1; i <= n; i++) 
    for(int j = m; j >= v[i]; j++){
    	f[j] = max(f[j],f[j - v[i]]+ w[i]);
    }    

二、完全背包問題

問題描述:

有 N 件物品和一個容量為 V 的背包,每件物品有各自的價值可以被選擇多次(0,1,2,3 … ),要求在有限的背包容量下,裝入的物品總價值最大。

樸素完全背包
  1. 轉(zhuǎn)態(tài)f[i,j]定義 :在前i個物品中選,總體積不超過j的最大價值。

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

    f[i,j] = max(f[i-1,j],f[i-1,j-v[i]]+w[i],f[i-1,j-2v[i]]+2w[i]…f[i-1,j-sv[i]]+sw[i])
    s表示能取到的最大值,不超過體積即可

    分析:

    ? 據(jù)題目要求,我們在考慮第i個物品的時候,我們可以不選,選1個,選2個。。??梢赃x到選不下為止。在這些所有轉(zhuǎn)態(tài)中,我們找到一種最大情況。

代碼

for(int i = 1 ; i<=n ;i++)   //考慮每一件物品
    for(int j = 0 ; j<=m ;j++)  //考慮體積
        for(int k = 0 ; k*v[i]<=j ; k++)  //考慮當前物品在當前體積的情況下,能選到的最大價值
            f[i][j] = max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
優(yōu)化一維的完全背包

1. 首先我們先來看如何優(yōu)化掉內(nèi)層循環(huán)

在上面樸素代碼最內(nèi)層的循環(huán)含義是找到放多個第i個物品可以可以使得當前體積獲得的價值最大,數(shù)學關(guān)系式如下:

f[i , j ] = max( f[i-1,j] , f[i-1,j-v]+w , f[i-1,j-2v]+2w , f[i-1,j-3v]+3w , …)

f[i,j-v]如下:

f[i , j-v]= max( f[i-1,j-v] , f[i-1,j-2v] + w , f[i-1,j-3v]+2*w , …)

我們發(fā)現(xiàn)上面兩個式子中,第一個式子的f[i-1,j-v]+w , f[i-1,j-2v]+2w , f[i-1,j-3v]+3w , … 可以用f[i,j-v]+w替換可得出如下遞推關(guān)系: f[i,j]=max(f[i,j-v]+w , f[i-1,j])

優(yōu)化一層循環(huán)后的代碼:

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

2. 01背包和完全背包的區(qū)別

? 這個代碼和01背包的代碼十分相似,唯一區(qū)別就是,01背包進行狀態(tài)計算的時候,每次用到的是上一層的狀態(tài),而完全背包是用本層已經(jīng)更新的狀態(tài)去更新。

f[i][j] = max(f[i][j],f[i-1][j-v[i]]+w[i]); //01背包

f[i][j] = max(f[i][j],f[i][j-v[i]]+w[i]); //完全背包問題

3. 一維完全背包的優(yōu)化

? 這個優(yōu)化和01背包幾乎一致,唯一的區(qū)別 在于內(nèi)層循環(huán)的順序。為什么? 參考上面

for(int i = 1 ; i<=n ;i++)
    for(int j = v[i] ; j<=m ;j++)//注意了,這里的j是從小到大枚舉,和01背包不一樣
     	f[j] = max(f[j],f[j-v[i]]+w[i]);

三、多重背包問題

問題描述:

有 N 件物品和一個容量為 V 的背包,每件物品有各自的價值可以被選擇多次但最多只能選擇S次,要求在有限的背包容量下,裝入的物品總價值最大。

樸素多重背包
  1. 轉(zhuǎn)態(tài)f[i,j]定義 :在前i個物品中選,總體積不超過j的最大價值。

  2. 狀態(tài)轉(zhuǎn)移方程
    f[i,j] = max(f[i-1,j],f[i-1,j-v[i]]+w[i],f[i-1,j-2v[i]]+2w[i]…f[i-1,j-v[i]]+sw[i])
    s表示能取到的最大值,其中 s<=S 并且 s*v[i] <= j 。

  3. 完全背包和多重背包的區(qū)別
    二者區(qū)別在于完全背包對于當前物品可以選無數(shù)個,直到背包裝不下為止。而多重背包是對完全背白的選擇進行了限制,每個物品只能選擇S個??梢岳斫獬?mark>有限制的完全背包問題。

for(int i = 0;i<n;i++){
	for(int j = 0;j<=m;j++){
		for(int k = 0;k<=s[i] && k*v[i] <= j;k++){
			f[i][j] = max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
		}
	}
}
多重背包的二進制優(yōu)化

1. 能不能用完全背包優(yōu)化方法來優(yōu)化呢?

  • f[i , j ] = max( f[i-1,j] , f[i-1,j-v]+w , f[i-1,j-2v]+2w , f[i-1,j-3v]+3w , … f[i-1,j-sv]+sw)
  • f[i , j-v]= max( f[i-1,j-v] , f[i-1,j-2v] + w , f[i-1,j-3v]+2w , … f[i-1,j-sv]+(s-1)w , f[i-1,j-(s+1)v]+sw)

由于f[i,j-v]中多了一項f[i-1,j-(s+1)v]+sw,所以 f[i-1,j-v]+w , f[i-1,j-2v]+2w , f[i-1,j-3v]+3w , … f[i-1,j-sv]+sw 這一坨的最大值不能用f[i,j-v]+w表示,因此不能采用與完全背包同樣的方法優(yōu)化。

2. 二進制優(yōu)化的思想以及為什么用這種方法?

1)有一個數(shù)13,我們把13分成1,2,4,6 這四個數(shù),我們可以拿這四個數(shù)可以表示任意1~13的數(shù)。

2)在清楚上一步之后,我們用上述思想轉(zhuǎn)換到本題。在正常背包的思路下,假設這一類物品有13個,我們要求出這一類物品的最大價值,需要考慮每一種情況,也就是枚舉14次(0~13)。假設我們現(xiàn)在將這13個物品打包成1,2,4,6,把他們看成4個單獨的物品,我們只需要考慮每件物品選與不選,就可以產(chǎn)生選0 ~ 13 其中的任意一種情況,而且只需要考慮4次。這就大大降低了我們枚舉的次數(shù)。

3)我們將每一類物品進行打包之后,我們對于打包之后的每一個物品的選與不選,可以轉(zhuǎn)化成01背包問題。

3. 最終實現(xiàn)代碼

#include<bits/stdc++.h>
using namespace std;
const int N = 15000;
int n,m;
int logs[N],w[N];
int f[N];
int main()
{
	cin>>n>>m;
    int cnt = 0;
	for(int i = 0;i<n;i++){
		int a,b,s;  	//a體積,b表示價值 ,s表示該類物品的個數(shù)
        cin>>a>>b>>s;
        int t = 1;
        while(s >= t){  //將每一類物品按二進制數(shù)打包
            cnt++;
            logs[cnt] = t * a;
            w[cnt] = t * b;
            s -= t;
            t *= 2;
        }
        if(t > 0){     //不足,單獨算一個
            cnt++;
            logs[cnt] = s * a;
            w[cnt] = s * b;
        }
        
    }
    for(int i = 1;i<=cnt;i++){
		for(int j = m;j>=logs[i];j--){
			f[j] = max(f[j],f[j-logs[i]]+w[i]);
        }
    }
    cout<<f[m]<<endl;
    return 0;
}
多重背包單調(diào)隊列優(yōu)化

1. f[i,j]當前最大價值是對于第i類物品,選擇0~S個(S是該類物品的最大個數(shù))這些情況下的價值的最大值

2. 根據(jù)上面的定義我們列出所有的情況。

 >f[i,j]=max(f[i-1,j],f[i-1,j-v]+w,f[i-1,j-2v]+2w,...,f[i-1,j-kv]+kw);
 >f[i,j-v]=max(        f[i-1,j-v],  f[i-1,j-2v]+w, ...,f[i-1,j-kv]+(k-1)w,f[i-1,j-(k+1)v]+kw);
 >...
 >f[i,r] = max(...)  r = j - 能取到的最大物品個數(shù)k * v ,也可以理解成j % v 的余數(shù)

3. 對體積0~m,我們可以分為v組,每組大小s,對于任何一個 j = v*s+ r,所以我們可以得到一個容量為s的滑動串窗口。對于體積為m,并在第i類物品的選擇下的情況(即f[i,m])

 f(i,r)=f[i,r]
 
 f(i,r+v)=max(f[i,r+v]-w,f[i,r])
 
 f(i,r+2v)=max(f[i,r+2v]-2w,f[i,r+v]-w,f[i,r])
 
 ?
 
 f(i,r+(s?1)v)=max(f[i,r+(s-1)v],f[i,r+(s - 2)v]+w,?,f[i,r]+(s?1)w)
 
 f(i,r+sv)=max(f[i,r+sv],f[i,r+(s-1)v]+w,?,f[i,r]+sw)               (滑動窗口已滿)
 
 f(i,r+(s+1)v)=max(f[i,r+(s+1)v],f[i,r+sv]+w,?,f[i,r+v]+sw)    (滑動窗口已滿)

?
 
 f(i,j?2v)=max(f[i,j?2v],f[i,j?3v]+w,?,f[i,j?(s+2)v]+sw)          (滑動窗口已滿)
 
 f(i,j?v)=max(f[i,j?v],f[i,j?2v]+w,?,f[i,j?(s+1)v]+sw)              (滑動窗口已滿)
 
 f(i,j)=max(f[i,j],f[i,j?v]+w,?,f[i, j?sv]+sw)                                (滑動窗口已滿)

4. 根據(jù)上面我們可以用一個單調(diào)隊列來維護我們需要用到的體積

  • 當超過我們?nèi)萘縮的時候,隊頭彈出一個元素
  • 當要插入的體積對應的最大值大于等于隊尾的最大值,要彈出隊尾,直到滿足為止
  • 插入當前體積

5. 每次入隊的值是 dp[j-kv] + kw計算w的倍數(shù),可以通過隊列存的體積與當前枚舉的體積之差模上v而得。

具體代碼如下:文章來源地址http://www.zghlxwxcb.cn/news/detail-463589.html

#include<bits/stdc++.h>
using namespace std;

const int N = 1010,M = 20010;  
int n,m;
int v[N],w[N],s[N];  //體積 價值 數(shù)量
int f[M],g[M];   	 //dp數(shù)組和備份數(shù)組
int q[M];   		 //維護滑動窗口的單調(diào)隊列
int main()
{
    cin>>n>>m;
    for(int i = 1;i<=n;i++) cin>>v[i]>>w[i]>>s[i];
    for(int i = 1;i<=n;i++){
        memcpy(g,f,sizeof f);
        for(int j = 0;j<v[i];j++){    //分成v[i]組
            int hh = 0,tt = -1;
            for(int k = j;k<=m;k += v[i]){
                while (hh <= tt && k - q[hh] > s[i] * v[i]) hh ++ ;
                while (hh <= tt && g[q[tt]] + (k - q[tt])/v[i] * w[i] <= g[k]) tt--;
                q[ ++ tt] = k;
                f[k] = g[q[hh]] + (k - q[hh]) / v[i] * w[i];
            }
        }
    }
    cout<<f[m]<<endl;
    return 0;
    
}

到了這里,關(guān)于背包問題分析代碼詳解【01背包+完全背包+多重背包】的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

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

領支付寶紅包贊助服務器費用

相關(guān)文章

  • 代碼隨想錄 Day35 動態(tài)規(guī)劃04 01背包問題和完全背包問題 LeetCode T416 分割等和子集

    代碼隨想錄 Day35 動態(tài)規(guī)劃04 01背包問題和完全背包問題 LeetCode T416 分割等和子集

    說到背包問題大家都會想到使用動規(guī)的方式來求解,那么為什么用動規(guī)呢, dp數(shù)組代表什么呢 ? 初始化是什么 , 遍歷方式又是什么 ,這篇文章筆者將詳細講解背包問題的經(jīng)典例題0-1背包問題和完全背包問題的解題方式,希望能幫助到大家 有人一提到背包問題就只會使用動態(tài)規(guī)劃來

    2024年02月06日
    瀏覽(123)
  • 【算法日志】動態(tài)規(guī)劃刷題:01背包問題,多重背包問題(day37,day38)

    【算法日志】動態(tài)規(guī)劃刷題:01背包問題,多重背包問題(day37,day38)

    目錄 前言 目標和(01背包) 一和零(01背包) 零錢兌換(多重背包) 排列總和(多重背包) 這兩天都是背包問題,其中的01背包的一些應用問題需要一定的數(shù)學建模能力,需要i將實際問題簡化成我們熟悉的背包問題;而這兩天的多重背包問題還算比較基礎,但也要我明白了

    2024年02月11日
    瀏覽(95)
  • 背包問題——01背包|完全背包

    背包問題——01背包|完全背包

    目錄 前言背包問題的歷史 ?01背包 ?1、題目 2、暴力解01背包 ?Ⅰ、代碼 3、動態(tài)規(guī)劃解01背包 Ⅰ、二維dp數(shù)組解01背包 1)dp數(shù)組的含義 ?2)遞推公式 ?3)dp數(shù)組的初始化 ?4)遍歷順序的討論 ?5、代碼 ?Ⅱ、一維數(shù)組解01背包 ?1)一維數(shù)組|滾動數(shù)組 ?2)一維數(shù)組的含義及遞

    2024年02月02日
    瀏覽(22)
  • 關(guān)于完全背包的解析以及完全背包與01背包的區(qū)別及代碼

    完全背包是什么呢?如果大家了解過01背包那么完全背包也是可以理解的。完全背包也是求一個固定容量的背包,能夠裝入物品的最大價值是多少,也就是說該背包最多能裝多少價值?和01背包不同的是,完全背包里所能裝的各個物品給定是無限的,也就是說同一個物品我們可

    2023年04月09日
    瀏覽(21)
  • 算法套路十四——動態(tài)規(guī)劃之背包問題:01背包、完全背包及各種變形

    算法套路十四——動態(tài)規(guī)劃之背包問題:01背包、完全背包及各種變形

    如果對遞歸、記憶化搜索及動態(tài)規(guī)劃的概念與關(guān)系不太理解,可以前往閱讀算法套路十三——動態(tài)規(guī)劃DP入門 背包DP介紹:https://oi-wiki.org/dp/knapsack/ 0-1背包:有n個物品,第i個物品的體積為w[i],價值為v[i],每個物品至多選一個, 求體積和不超過capacity時的最大價值和,其中i從

    2024年02月10日
    瀏覽(89)
  • 多重背包問題(詳解二進制優(yōu)化原理)

    多重背包問題(詳解二進制優(yōu)化原理)

    這道題同樣是背包問題,那么它也同樣滿足三個性質(zhì):重疊子問題、最優(yōu)子結(jié)構(gòu)以及無后效性。那么這樣的話,我們依然可以使用動態(tài)規(guī)劃的思路去分析這道題目。那么動態(tài)規(guī)劃的分析主要分為兩步:狀態(tài)轉(zhuǎn)移方程的書寫以及循環(huán)的設計。 (1)狀態(tài)表示: 我們在前面的兩篇

    2024年02月14日
    瀏覽(19)
  • 動態(tài)規(guī)劃-----背包類問題(0-1背包與完全背包)詳解

    動態(tài)規(guī)劃-----背包類問題(0-1背包與完全背包)詳解

    目錄 什么是背包問題? 動態(tài)規(guī)劃問題的一般解決辦法: 0-1背包問題: 0 - 1背包類問題? 分割等和子集:? 完全背包問題:? 完全背包類問題 零錢兌換II: 背包問題(Knapsack problem)是一種組合優(yōu)化的NP完全問題。 問題可以描述為:給定一組物品,每種物品都有自己的重量和價格

    2024年04月17日
    瀏覽(29)
  • 動態(tài)規(guī)劃:0-1背包、完全背包問題 | 詳細原理解釋 | 代碼及優(yōu)化(C++)

    動態(tài)規(guī)劃:0-1背包、完全背包問題 | 詳細原理解釋 | 代碼及優(yōu)化(C++)

    目錄 01背包 問題描述: 簡單描述就是: 解析: 遞推公式: dp數(shù)組的初始化: 遍歷順序: 圖解: 實現(xiàn)代碼: dp數(shù)組初始化: 遍歷: 優(yōu)化: 原理: 遞推公式: 遍歷順序: 實現(xiàn)代碼: 初始化: 遍歷: 完全背包 問題描述: 解析: 實現(xiàn)代碼: ????????01背包是在M件物品

    2024年02月11日
    瀏覽(21)
  • 算法 DAY45 動態(tài)規(guī)劃07 70. 爬樓梯 322. 零錢兌換 279. 完全平方數(shù) 139. 單詞拆分 多重背包

    算法 DAY45 動態(tài)規(guī)劃07 70. 爬樓梯 322. 零錢兌換 279. 完全平方數(shù) 139. 單詞拆分 多重背包

    和377. 組合總和 Ⅳ (opens new window)基本就是一道題了。 本題代碼不長,題目也很普通,但稍稍一進階就可以考察完全背包 動態(tài)規(guī)劃五部曲 1、確定dp[j]的含義 dp[j] 湊成 j 的最少硬幣的個數(shù) 2、確定遞推公式 比如想湊成3, 如果手里有1,那么最小個數(shù)就是dp[2]+1 如果手里有2,那

    2024年02月02日
    瀏覽(35)
  • 【LeetCode動態(tài)規(guī)劃#08】完全背包問題實戰(zhàn)與分析(零錢兌換II)

    力扣題目鏈接(opens new window) 給定不同面額的硬幣和一個總金額。寫出函數(shù)來計算可以湊成總金額的硬幣組合數(shù)。假設每一種面額的硬幣有無限個。 示例 1: 輸入: amount = 5, coins = [1, 2, 5] 輸出: 4 解釋: 有四種方式可以湊成總金額: 5=5 5=2+2+1 5=2+1+1+1 5=1+1+1+1+1 示例 2: 輸入: amount = 3

    2023年04月19日
    瀏覽(47)

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

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

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

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

二維碼1

領取紅包

二維碼2

領紅包