1、01背包问题
将质量为wi、价值为vi的n种物品,每种物品只有一个,放入最大承重为m的背包中,求背包所能得到的最大价值。
动态转移方程:f[i][j]=max(f[i-1][j],f[i-1][j-wi]+vi);
f[i][j]的含义是从前i种物品中取物品放入最大承重为j的背包中,所能得到的最大价值。
它等于下述两种情况的最大值:
1、忽略第i种物品,从前i-1种物品中取物品放入最大承重为j的背包中,所能得到的最大价值。
2、直接将第i种物品放入背包中,从前i-1种物品中取物品放入最大承重为j-wi的背包中,所能得到的最大价值。
时间复杂度为O(N*M)
代码如下:
#include <iostream>
#include <math.h>
int num,m;
int v[100],w[100];
int dp[100][100];
void solve();
using namespace std;
int main(){
cin>>num>>m;
int i,j;
for(i=0;i<num;i++){
cin>>w[i]>>v[i];
}
for(i=0;i<num;i++)dp[i][0]=0;
for(i=0;i<m;i++)dp[0][i]=0;
solve();
/*输出dp[i][j]
for(i=0;i<num+1;i++){
for(j=0;j<=m;j++)
cout<<dp[i][j];
cout<<endl;
}
*/
cout<<dp[num][m]<<endl;
return 0;
}
void solve(){
int i,j;
for(i=0;i<num;i++){
for(j=0;j<=m;j++){
if(j<w[i])dp[i+1][j]=dp[i][j];//动态规划
else dp[i+1][j]=max(dp[i][j],dp[i][j-w[i]]+v[i]);
}
}
}
上述为背包承重小于等于m时所得到的最大价值。
当要求背包承重恰为m时,背包所能得到的最大价值,此时除了将dp[0][i]和dp[i][0]初始化为0外,还需将剩余dp初始化为负无穷。当dp[i][j]为负无穷时,表示无解。
空间优化:可将dp[i][j]二维数组优化为一维数组。时间复杂度为O(N*M)
for(i=0;i<num;i++)
for(j=n;j>0;j--)
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
实际代码:
#include <iostream>
#include <math.h>
int num,m;
int v[100],w[100];
int dp[100];
void solve();
using namespace std;
int main(){
cin>>num>>m;
int i,j;
for(i=0;i<num;i++){
cin>>w[i]>>v[i];
}
for(i=0;i<m;i++)dp[i]=0;
solve();
cout<<dp[m]<<endl;
return 0;
}
void solve(){
int i,j;
for(i=0;i<num;i++){
for(j=m;j>=0;j--){
if(w[i]<=j){
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
}
cout<<dp[j];
}
cout<<endl;
}
}
2、完全背包问题
将质量为wi、价值为vi的n种物品,每种物品有无限多件,放入最大承重为m的背包中,求背包所能得到的最大价值。
动态转移方程:f[i][j]=max(max(f[i-1][j-k*w[i]]+k*v[i]| 0<=k*w[i]<=j);
状态数为n*m个,但求解每个状态的时间复杂度不再为常数,而是为o(j/w[i]),所以时间复杂度高于O(n*m);
#include <iostream>
int num,m;
int v[100],w[100];
int dp[100][100];
void solve();
using namespace std;
int main(){
cin>>num>>m;
int i,j;
for(i=0;i<num;i++){
cin>>w[i]>>v[i];
}
for(i=0;i<=num;i++)dp[i][0]=0;
for(i=0;i<=m;i++)dp[0][i]=0;
solve();
cout<<dp[num][m]<<endl;
return 0;
}
void solve(){
int i,j,k;
for(i=0;i<num;i++){
for(j=0;j<=m;j++){
if(j<w[i]){
dp[i+1][j]=dp[i][j];
}else{
int max1=0;
for(k=0;k<=j/w[i];k++){
int n=dp[i][j-k*w[i]]+k*v[i];
if(max1<n)
max1=n;
}
dp[i+1][j]=max1;
}
cout<<dp[i+1][j];//输出验证
}
cout<<endl;
}
}
空间优化一维数组。
状态转移方程:
for(i=0;i<num;i++)
for(j=1;j<=m;j++)
f[j]=max(f[j],f[j-w[j]]+v[j]);复杂度O(num*n)
#include <iostream>
int num,m;
int v[100],w[100];
int dp[100];
using namespace std;
int main(){
cin>>num>>m;
int i,j;
for(i=0;i<num;i++){
cin>>w[i]>>v[i];
}
for(i=0;i<num;i++)
for(j=1;j<=m;j++)
if(j>=w[i])
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);//dp[i+1][j]=max(dp[i][j],dp[i+1][j-k*w[i]]+k*v[i]);
cout<<dp[m];
return 0;
}
3、多重背包问题
将质量为wi、价值为vi的n种物品,每种物品有ci件,放入最大承重为m的背包中,求背包所能得到的最大价值。
类似完全背包问题:
二维状态转移方程:f[i][j]=max(f[i-1][j-k*mi]+k*vi| 0<=k<=ci);
复杂度:O(j*(c1+c2+...+cn))
#include <iostream>
int num,m;
int v[100],w[100],c[100];
int dp[1001][1001];
void solve();
using namespace std;
int main(){
cin>>num>>m;
int i,j;
for(i=0;i<num;i++){
cin>>w[i]>>v[i]>>c[i];
}
for(i=0;i<=num;i++)dp[i][0]=0;
for(i=0;i<=m;i++)dp[0][i]=0;
solve();
cout<<dp[num][m]<<endl;
return 0;
}
void solve(){
int i,j,k;
for(i=0;i<num;i++){
for(j=0;j<=m;j++){
if(w[i]>j){
dp[i+1][j]=dp[i][j];
}else{
int max1=0;
for(k=0;k<=c[i];k++){
if(j<k*w[i]) break;//必须能够有那么多重量空间
int n = dp[i][j-k*w[i]]+k*v[i];
if(max1<n)
max1 = n;
}
dp[i+1][j]=max1;
}
cout<<dp[i+1][j]<<" ";//输出中间过程验证
}
cout<<endl;
}
}
空间优化一维数组:
#include<stdio.h>
#include<algorithm>
using namespace std;
int w[1002], v[1002], c[1002];
int dp[1002];
int n, m;
int main()
{
scanf("%d%d",&n,&m);
for (int i = 1; i <= n; i++)
cin<<w[i]<<v[i]<<c[i];
for (int i = 1; i <= n; i++){
for (int j = m; j >= 0; j--){
for (int k = 0; k <= c[i]; k++)
{
if (j-k*w[i]<0) break;
f[j] = max(f[j],f[j-k*w[i]]+k*v[i]);//f[i][j]=max(f[i-1][j],f[i-1][j-k*w[i]]+k*v[i]);
}
printf("%d ",f[j]);
}
printf("\n");
}
printf("%d\n",f[m]);
return 0;
}
时间复杂度优化:可通过将第i种物品的ci件进行划分,化分为1,2,4,8,...,2^(k-1),ci-2^k+1,且k是满足n[i]-2^k+1>0的最大整数。此时将复杂度变为O(j*(log c1 + log c2 +log c3... +log cn))。
四、混合三种背包问题
for(i =0;i<num;i++)
{
if(ni为01背包问题){
for(j=N;j>=wi;j++)
dp[j]=max(dp[j],dp[j-wi]+vi);
}else if(ni为完全背包问题){
for(j=wi;j<=N;j++)
dp[j]=max(dp[j],dp[j-wi]+vi);
}else if (ni为部分背包问题){
for (int j = m; j >= 0; j--){
for (int k = 0; k <= c[i]; k++)
{
if (j-k*w[i]<0) break;
f[j] = max(f[j],f[j-k*w[i]]+k*v[i]);
}//不是最优解 可采用O(j*(log c1 + log c2 +log c3... +log cn))
}
}