动态规划之背包DP

\(\small{(本文统一将c[i]视作cost,w[i]视作worth,下面的代码用这两个变量表示费用和价值)}\)

01背包

描述:

有n个物品,每个物品只有一件,第i个物品体积为vi,价格为ci。现在有一个体积为V的背包,请你从n件物品里选出若干件放进背包里,使得背包里的物品价值最大。

思路:

01背包的特点是:每种物品只有一件,可以选择放或不放。我们可以根据此特点进行动态规划(DP),设f[i][j]表示前i件物品放入一个容量为j的背包中可以获得的最大价值,则易得状态转移方程

dp[i][j]=max(dp[i-1][j],dp[i-1][j-c[i]]+w[i])

(详解:在“将前i个物品放入容量为j的背包中”的这个子问题中,由题意,我们只有\(\textbf{放}\)\(\textbf{不放}\)两种选择,那么就转化为一个只与前i-1个物品有关的问题。如果不放第i件物品,那么就是“前i-1件物品放入容量为j的背包中”,最大价值为f[i-1][j];如果放第i件物品,那么就是“前i-1件物品放入剩下的容量为j-c[i]的背包中”,最大价值为f[i-1][j-c[i]]+w[i])

由此可得01背包的\(\textbf{最原始}\)代码

code

$\large\textbf{code}$
#include<bits/stdc++.h>
using namespace std;
#define N 1010
#define Elaina 0
int n,m,V,c[N],w[N],dp[N][N],ans=0;
void bag(){
	for(int i=1;i<=n;i++){
		for(int j=1;j<=V;j++){
			dp[i][j]=dp[i-1][j];
			if(j>=c[i]){
				dp[i][j]=max(dp[i-1][j],dp[i-1][j-c[i]]+w[i]);
			}
		}
	}
}
int main(){
	cin>>n>>V;
	for(int i=1;i<=n;i++){
		cin>>c[i]>>w[i];
	}
	bag();
	cout<<dp[n][V];
	return Elaina;
} 

以上代码的时间和空间的复杂度均为O(V*N),其中时间已经不能进一步优化了,但是空间可以

滚动数组优化code

code
#include<bits/stdc++.h>
using namespace std;
#define N 1010
#define Elaina 0
int n,m,V,c[N],w[N],ans=0;
int dp[2][N];//滚动数组优化 只开2行数组 
void bag(){
	for(int i=1;i<=n;i++){
		for(int j=1;j<=V;j++){
			dp[i&1][j]=dp[(i-1)&1][j];
			if(j>=c[i]){
				dp[i&1][j]=max(dp[i&1][j],dp[(i-1)&1][j-c[i]]+w[i]);
			}
		}
	}
}
int main(){
	cin>>n>>V;
	for(int i=1;i<=n;i++){
		cin>>c[i]>>w[i];
	}
	bag();
	cout<<dp[n&1][V];
	return Elaina;
}

一维优化code

code
#include<bits/stdc++.h>
using namespace std;
#define N 1010
#define Elaina 0
int n,m,V,c[N],w[N],ans;
int dp[N];//一维数组优化
void bag(){
	for(int i=1;i<=n;i++){
		for(int j=V;j>=c[i];j--){
			dp[j]=max(dp[j],dp[j-c[i]]+w[i]);
		}
	}
}
int main(){
	cin>>n>>V;
	for(int i=1;i<=n;i++){
		cin>>c[i]>>w[i];
	}
	bag();
	cout<<dp[V];
	return Elaina;
}

完全背包

描述:

设有n种物品,每种物品有一个重量及一个价值。但每种物品的数量是无限的,同时有一个背包,最大载重量为M,今从n种物品中选取若干件(同一种物品可以多次选取),使其重量的和小于等于M,而价值的和为最大。

思路:

完全背包的特点是:每种物品有无数件,可以选择放若干件或不放。
设k为取的物品的数量,依据01背包思路,易得状态转移方程为

dp[i][j]=max(dp[i-1][j],dp[i-1][j-k*c[i]]+k*w[i])

code

$\large\textbf{code}$
#include<bits/stdc++.h>
using namespace std;
#define N 10100
#define Elaina 0
int n,m,V,c[N],w[N],dp[N][N],ans=0;
void bag(){
	for(int i=1;i<=n;i++){
		for(int j=1;j<=V;j++){
			for(int k=0;k*c[i]<=j;k++){
				dp[i][j]=max(dp[i-1][j],dp[i-1][j-k*c[i]]+k*w[i]);
			}
		}
	}
}
int main(){
	cin>>V>>n;
	for(int i=1;i<=n;i++){
		cin>>c[i]>>w[i];
	}
	bag();
	cout<<dp[n][V];
	return Elaina;
}

一维优化code

code
#include<bits/stdc++.h>
using namespace std;
#define N 10100
#define Elaina 0
int n,m,V,c[N],w[N],dp[N],ans=0;
void bag(){
	for(int i=1;i<=n;i++){
		for(int j=c[i];j<=V;j++){
			dp[j]=max(dp[j],dp[j-c[i]]+w[i]);
		}
	}
}
int main(){
	cin>>V>>n;
	for(int i=1;i<=n;i++){
		cin>>c[i]>>w[i];
	}
	bag();
	cout<<dp[V];
	return Elaina;
}

多重背包

描述:

有N种物品和一个容量为V的背包。第i ii种物品最多有p[i]件可用,每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

思路:

这题目和完全背包问题很类似。基本的方程只需将完全背包问题的方程略微一改即可,因为对于第i种物品有p[i]+1种策略:取0件,取1件……取p[i]件。令dp[i][j]表示前i种物品恰放入一个容量为j的背包的最大价值,则有状态转移方程:

dp[i][j]=max(dp[i][j],dp[i-1][j-k*c[i]]+k*w[i])

code

$\large\textbf{code}$
#include<bits/stdc++.h>
using namespace std;
#define N 10100
#define Elaina 0
int n,m,V,c[N],w[N],dp[N][N],s[N];
void bag(){
	for(int i=1;i<=n;i++){
		for(int j=1;j<=V;j++){
			for(int k=0;k<=s[i]&&k*c[i]<=j;k++){
				dp[i][j]=max(dp[i][j],dp[i-1][j-k*c[i]]+k*w[i]);
			}
		}
	}		            
}
int main(){
	cin>>n>>V;
	for(int i=1;i<=n;i++){
		cin>>c[i]>>w[i]>>s[i];
	}
	bag();
	cout<<dp[n][V];
	return Elaina;
}

一维优化code

code
#include<bits/stdc++.h>
using namespace std;
#define N 1010
#define Elaina 0
int n,m,V,c[N],w[N],dp[N],s[N];
void bag(){
	for(int i=1;i<=n;i++){
		for(int j=V;j>=0;j--){
				for(int k=0;k<=s[i]&&k*c[i]<=j;k++){
					dp[j]=max(dp[j],dp[j-k*c[i]]+k*w[i]);
				}
			} 
	}		            
}
int main(){
	cin>>n>>V;
	for(int i=1;i<=n;i++){
		cin>>c[i]>>w[i]>>s[i];
	}
	bag();
	cout<<dp[V];
	return Elaina;
}

二进制优化

详解

code

code
#include<bits/stdc++.h>
using namespace std;
#define N 50100
#define ll long long
#define inf 0x3f3f3f3f
#define Elaina 0
int idx=0,n,V,c[N],w[N],dp[N];
void bag(){
	for(int i=1;i<=idx;i++){
		for(int j=V;j>=c[i];j--){
			dp[j]=max(dp[j],dp[j-c[i]]+w[i]);
		}	
	}		            
}
int main(){
	cin>>n>>V;
	for(int i=1;i<=n;i++){
		int a,b,s;
		cin>>a>>b>>s;
		int k=1;
		if(!s){
			s=V/a+1;
		}
		while(k<=s){
			idx++;
			v[idx]=a*k;
			w[idx]=b*k;
			s-=k;
			k*=2;
		} 
		if(s){
			idx++;
			v[idx]=a*s;
			w[idx]=b*s;
		}
	}
	bag();
	cout<<dp[V];
	return Elaina;
}

混合背包

描述:

一个旅行者有一个最多能用V公斤的背包,现在有n件物品,它们的重量分别是W1,W2,...,Wn,它们的价值分别为C1,C2,...,Cn。有的物品只可以取一次(01背包),有的物品可以取无限次(完全背包),有的物品可以取的次数有一个上限(多重背包)。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

(就是把前01背包、完全背包、多重背包搓起来 搓吧搓吧就出来了(^_−)☆)

直接请出代码君

code
#include<bits/stdc++.h>
using namespace std;
#define N 10100
#define inf 0x3f3f3f3f
#define Elaina 0
int idx=0,n,V,c[N],w[N],dp[N],s[N],ans=inf;
void bag(){
	for (int i=1; i<=n; i++){
		if(s[i]==1){
			for(int j=V;j>=c[i];j--){
				dp[j]=max(dp[j],dp[j-c[i]]+w[i]);
			}
		}
		else if(s[i]==0){
			for(int j=c[i];j<=V;j++){
				dp[j]=max(dp[j],dp[j-c[i]]+w[i]);
			}
		}else{
			for(int j=V;j>=0;j--){
				for(int k=0;k<=s[i]&&k*c[i]<=j;k++){
					dp[j]=max(dp[j],dp[j-k*c[i]]+k*w[i]);
				}
			} 
		} 
	}
}
int main(){
	cin>>V>>n;
	for(int i=1;i<=n;i++){
		cin>>c[i]>>w[i]>>s[i];
	}
	bag();
	cout<<dp[V];
	return Elaina;
}

分组背包

描述:

一个旅行者有一个最多能用V公斤的背包,现在有n件物品,它们的重量分别是W1,W2,...,Wn,它们的价值分别为C1,C2,...,Cn。这些物品被划分为若干组,每组中的物品互相冲突,最多选一件。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大

死路:

这个问题变成了每组物品有若干种策略:是选择本组的某一件,还是一件都不选。设dp[k][j]表示前k组物品花费费用j的最大价值,则有状态转移方程为:

dp[k][j]=max(dp[k][j],dp[k-1][j-c[i]]+w[i]

一维优化code

code
#include<bits/stdc++.h>
using namespace std;
#define N 10100
#define ll long long
#define inf 0x3f3f3f3f
#define Elaina 0
int n,t,V,T,c[N],w[N],dp[N],g[N][N];
void bag(){
	for (int i=1; i<=T; i++){
		for(int j=V;j>=0;j--){
			for(int k=1;k<=g[i][0];k++){
				int x=g[i][k];
				if(j>=c[x]){
					dp[j]=max(dp[j],dp[j-c[x]]+w[x]);
				}
			}
		}
	}
}
int main(){
	cin>>V>>n>>T;
	for(int i=1;i<=n;i++){
		cin>>c[i]>>w[i]>>t;
		g[t][++g[t][0]]=i;
	}
	bag();
	cout<<dp[V];
	return Elaina;
}

二维背包

描述:

对于每件物品,具有两种不同的费用;选择这件物品必须同时付出这两种代价;对于每种代价都有一个可付出的最大值(背包容量)。问怎样选择物品可以得到最大的价值。设第i ii件物品所需的两种代价分别为c[i]和g[i]。两种代价可付出的最大值(两种背包容量)分别为V和M。物品的价值为w[i]。

死路:

费用加一维,状态也加一维即可。设dp[i][j][k]表示前i件物品付出的代价为j和k是的最大价值,易得状态转移方程为

dp[i][j][k]=max(dp[i-1][j][k],dp[i-1][j-c[i]][k-g[i]]+w[i]

二维优化code

code
#include<bits/stdc++.h>
using namespace std;
#define N 10100
#define Elaina 0
int M,n,V,c[N],w[N],dp[N][N],g[N];
void bag(){
	for(int i=1;i<=n;i++)
	 	for(int j=V;j>=c[i];j--)
	  		for(int k=M;k>=g[i];k--)
	   	  		dp[j][k]=max(dp[j][k],dp[j-c[i]][k-g[i]]+w[i]);

}
int main(){
	cin>>n>>V>>M;
	for(int i=1;i<=n;i++){
		cin>>c[i]>>g[i]>>w[i];
	}
	bag();
	cout<<dp[V][M];
	return Elaina;
}

有依赖的背包(树上背包)

鸽一会 咕咕 ヾ(o・ω・)ノ

都看到这了,真的不点个赞吗(>ω<*)

posted @ 2024-02-17 15:21  Elaina_0  阅读(81)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end