揹包問題

思路來源:https://blog.csdn.net/yoer77/article/details/70943462

(一)01揹包

思路分析:核心算法就是狀態轉移,開一個二維數組dp[n][w],n是物品個數,w是總承重,例如有5件物品,而揹包總承重是10,就需要開一個dp[5+1][10+1]數組,裏面存value

具體代碼:

for(int i=1;i<=N;i++)//i是物品數 
		for(int j=1;j<=W;j++)//j是物品承重 
		{
			if(j<w[i])
				dp[i][j]=dp[i-1][j];
			else
				dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
		}

一層for 從第一件物品開始遍歷所有的物品

二層for 遍歷承重1—w 

對於每一件物品

if 承重<當前物品重量(太沉了裝不下):讓此時value=上一件物品的此時value(就是不把這件物品放進去)

else(如果能裝下):計算上一件物品的此時value和把這件物品塞進來的value比較,哪個大取哪個;

最後當把所有的物品遍歷完後,dp[n][]這個數組裏就是每一種承重下的最大value,遍歷一遍則得到最大value;

遍歷代碼:

for(int i=1;i<=N;i++)
	{
		for(int j=1;j<=W;j++)
		{
			cout<<dp[i][j]<<" ";
		}
		cout<<endl;
	}

case 1:

row1:5 件物品 總承重10

row2-rown+1:value、weight

else:dp數組[1-n][1-w]

下面講一下空間複雜度優化後算法

根據大佬們的說法:第i行存的是前i種物品下j承重的最大value,那麼如果我們求的是所有物品(也就是所謂的n件物品)的最大value的話,其實我們只需要用最後一行來存就可以了,但是問題在於我們在討論第i件物品時,需要i-1時的狀態來幫助我們往下推,那麼怎麼解決這個問題呢?其實很簡單(簡單個P),就是在每次討論第i件物品的開始(第二個for循環開始),這時的數組裏存的還是i-1時的狀態,我們每次遍歷時其實用到的都是[i-1][j]或者[i-1][j-w[i]],能夠發現用的都是比此稱重下小的狀態

所以!我們只需要反着遍歷就可以了,從承重w—1,這樣就ok了,我們在用到之前狀態時,還沒覆蓋;

具體代碼如下:

for(int i=1;i<=N;i++)//i是物品數 
	{
		for(int j=W;j>=0;j--)//j是物品承重 
		{
			if(j<w[i])continue;
			else
				dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
		}
	}

 

Case 2:

樣例仍是Case 1的樣例,但數組卻只存儲了最後一行,空間複雜度下降到O(n)

(二)完全揹包

完全揹包其實就是在01揹包的基礎上構成的

(The original thinking)

01揹包每次更新一件物品時,是dp[當前承重]與dp[當前承重-該件物品的重量]+該物品value比較,這是選或不選兩種情況;

而完全揹包就是在更新的時候不停地塞該件物品,直到塞不進去,所以時間複雜度上要高一點,因爲每件物品是從0-k件

(The second thinking)

根本不用那麼麻煩,我們重複計算了好多過程,例如當 w=3時,dp[7]=max(dp[7],dp[4]+v,dp[1]+2*v),但是這時的dp[4]就是最優情況,我們直接用dp[4]的answer+value[7]比較就可以推出dp[7]的大小

(想了好長時間,最後感覺好像dp[7]裏,3就沒法拿一件,3如果拿一件有兩種情況:

dp[4]更新,dp[7]未更新:dp[4]更新說明在滿足3時必拿3所以肯定拿兩件

dp[4]未更新,dp[7]更新):dp[4]未更新說明滿足3時不拿3而拿其他物品,所以肯定拿0件)

具體代碼如下:

#include<iostream>
#include<vector>
#include<cmath>
#define MAX_N 100
using namespace std;
struct thing{
	int v;
	int w;
};
int dp[MAX_N];//dp[i]±íʾµÄÊÇÇ°i¼þÎïÆ·µÄ×î´óvalue 
int main()
{
	int N,W;//¼þÊý ×î´ó³ÐÖØ  
	cin>>N>>W;
	vector<thing>tg;
	for(int i=0;i<N;i++)
	{
		thing a;
		cin>>a.w>>a.v;
		tg.push_back(a);
	}
	for(int i=0;i<N;i++)
	{
		int k=0;
		int v=tg[i].v;
		int w=tg[i].w;
		for(int j=0;j<=W;j++)
		{
			if(j<w)continue;//³ÐÖØ<ÎïÆ·w 
			dp[j]=max(dp[j],dp[j-w]+v); 
		}
	}
	int max=0;
	for(int i=1;i<=W;i++)
	{
		if(dp[i]>max)max=dp[i];
		cout<<dp[i]<<" ";
	}
		
	cout<<endl<<max<<endl;
 } 

(三)完全揹包:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章