揹包

sad_lin發表於2024-09-05

01 揹包

\(n\) 件物品,每件物品有權值和重量,給出揹包體積 \(V\),從這些物品中挑選若干件(只能選一次)放入揹包,使得揹包內物品的總重量不超過 \(V\),問能可以得到的最大權值。

\(dp[i][j]\) 選取前 \(i\) 件物品重量為 \(j\) 能取得的最大的權值,可以得到轉移方程 \(dp[i][j]=max(dp[i-1][j-w[i]]+val[i],dp[i-1][j]);\)

因為 i 只與 i-1 有關所以考慮滾動陣列:

交替滾動

用 now 表示現在的陣列,用 old 表示上一個陣列,交替使用。

#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int t,m;
int w[202],val[202];
int dp[3][10010];

int main(){
	ios::sync_with_stdio(false);
	cin>>t>>m;
	for(int i=1;i<=m;i++){
		cin>>w[i]>>val[i];
	}
	int now=0,old=1;
	for(int i=1;i<=m;i++){
		swap(old,now);
		for(int j=0;j<=t;j++){
			if(j>=w[i]){
				dp[now][j]=max(dp[old][j],dp[old][j-w[i]]+val[i]);
			}
			else{
				dp[now][j]=dp[old][j];
			}
		}
	}
	cout<<dp[now][t];
    return 0;
}

自我滾動

因為體積只會向小減少,所以從後向前遍歷容量以確保每個數物品只選一次,上一次的陣列儲存的數(i-1) 結果全部就在這個陣列裡,這樣空間壓縮到一維。

#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int t,m;
int w[202],val[202];
int dp[10010];

int main(){
	ios::sync_with_stdio(false);
	cin>>t>>m;
	for(int i=1;i<=m;i++){
		cin>>w[i]>>val[i];
	}
	for(int i=1;i<=m;i++){
		for(int j=t;j>=w[i];j--){
			dp[j]=max(dp[j],dp[j-w[i]]+val[i]);
		}
	}
	cout<<dp[t];
    return 0;
}

完全揹包

\(n\) 件物品,每件物品有權值和重量,給出揹包體積 \(V\),從這些物品中挑選若干件(可以多次同選一個)放入揹包,使得揹包內物品的總重量不超過 \(V\),問能可以得到的最大權值。

我們對 01 揹包的程式碼進行更改為從小到大列舉容量,因為在計算某個容量時,可以確保之前計算的狀態已經包含了該物品之前選擇的次數,這樣就能夠正確地累積多個相同物品的價值。

#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int t,m;
int w[202],val[202];
int dp[10010];

int main(){
	ios::sync_with_stdio(false);
	cin>>t>>m;
	for(int i=1;i<=m;i++){
		cin>>w[i]>>val[i];
	}
	for(int i=1;i<=m;i++){
		for(int j=w[i];j<=t;j++){
			dp[j]=max(dp[j],dp[j-w[i]]+val[i]);
		}
	}
	cout<<dp[t];
    return 0;
}

多重揹包

\(n\) 件物品,每件物品有權值和重量,給出揹包體積 \(V\),從這些物品中挑選若干件(給出共有幾個)放入揹包,使得揹包內物品的總重量不超過 \(V\),問能可以得到的最大權值。

考慮將每個物品進行二進位制拆分,拆完後就轉化為 01 揹包問題。(因為是二進位制,所以可以保證可以取到任意組合)

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int t,m;

int w[N],val[N];
long long dp[N];
int main(){
	ios::sync_with_stdio(false);
	cin>>m>>t;
	int a,b,c;
	int cnt=0;
	for(int i=1;i<=m;i++){
		cin>>a>>b>>c;
		
		for(int j=1;j<=c;j<<=1){//二進位制 
			w[++cnt]=j*a;//第cnt個揹包的重量 
			val[cnt]=j*b;//計算權值 
			c-=j;//計算剩餘 
		}
		if(c){//剩餘單獨分組 
			w[++cnt]=c*a;
			val[cnt]=c*b;
		}
	}
	for(int i=1;i<=cnt;++i){//01揹包選擇 
		for(int j=t;j>=w[i];--j){
			dp[j]=max(dp[j],dp[j-w[i]]+val[i]);
		}
	}
	cout<<dp[t];
    return 0;
}

相關文章