一、三种背包的概念区分
01背包: 有N件物品和一个容量为V的背包。每种物品均只有一件。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。
完全背包: 有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
多重背包: 有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
比较三个题目,会发现不同点在于每种背包的数量,01背包是每种只有一件,完全背包是每种无限件,而多重背包是每种有限件。
01背包:
有N件物品和一个容量为V的背包。每种物品均只有一件。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。
无优化
int n,m; //n表示几组数,m表示最多的重量
int w[],v[]; //重量和价值
int dp[m+5][m+5];
for(int i=0;i<n;i++)
{
for(int 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-w[i]]+v[i],dp[i][j]);
}
}
}
一维数组优化:
int n,m; //n表示几组数,m表示最多的重量
int w[],v[]; //重量和价值
int dp[m+5];
for(int i=0;i<n;i++)
{
for(int j=m;j>=w[i];j--)
{
dp[j]=max(dp[j-w[i]]+v[i],dp[j]);
}
}
更进一步的常数优化:(自己没怎么用过)
int n,m; //n表示几组数,m表示最多的重量
int w[],v[]; //重量和价值
int dp[m+5];
for(int i=0;i<n;i++)
{
sumw+=w[i];
bound=max(m-sumw,w[i]);
for(int j=m;j>=bound;j--)
{
if(j>=w[i])
dp[j]=max(f[j],f[j-w[i]]+v[i]);
}
}
完全背包:
有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
二维:
int n,m; //n表示几组数,m表示最多的重量
int w[],v[]; //重量和价值
int dp[m+5][m+5];
for(int i=0;i<n;i++)
{
for(int 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+1][j-w[i]]+v[i]);
}
}
一维:
int n,m; //n表示几组数,m表示最多的重量
int w[],v[]; //重量和价值
int dp[m+5];
for(int i=0;i<n;i++)
{
for(int j=m;j<=w[i];j++)
{
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
}
}
多重背包:
有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
最朴素二维:
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 101;
int c[maxn], w[maxn], m[maxn];
int dp[maxn][maxn]={0};
int main()
{
int n, v;
cin>>n>>v;
for (int i=1;i<=n;i++)
{
cin>>c[i]>>w[i]>>m[i];
}
for (int i=1;i<=n;i++)
{
for (int j=0;j<=v;j++) //v->0也行
{
for (int k=0;k*c[i]<=j && k <= m[i];k++)
{
dp[i][j] = max(dp[i-1][j-k*c[i]] + k*w[i], dp[i][j]);
}
}
}
cout<<dp[n][v]<<endl;
return 0;
}
最朴素一维:
#include <stdio.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=1001;
int main()
{
int N,V;
int v[maxn],w[maxn],s[maxn]; //v是体积,w是价值,s是个数
int dp[maxn];
scanf("%d %d",&N,&V);
for(int i=0;i<N;i++)
{
scanf("%d %d %d",&v[i],&w[i],&s[i]);
}
for(int i=0;i<N;i++)
{
for(int j=V;j>=v[i];j--)
{
for(int k=1;k<=s[i]&&k*v[i]<=j;k++) //循环的是个数来着
{
dp[j]=max(dp[j],dp[j-k*v[i]]+k*w[i]);
}
}
}
printf("%d\n",dp[V]);
return 0;
}
二进制优化:
#include <stdio.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=1001;
const int maxnn=25000;
int main()
{
int n,m;
int w[maxnn],v[maxnn];//质量、价值 拆完的
int w1[maxn],v1[maxn],s1[maxn];//输入进去的
int dp[maxn+maxn]={0};
scanf("%d %d",&n,&m);
for(int i=0;i<n;i++)
{
scanf("%d %d %d",&w1[i],&v1[i],&s1[i]);
}
int total=0;
for(int i=0;i<n;i++)
{
for(int j=1;j<s1[i];j<<=1) //二进制拆分 j从1开始2333
{
w[total]=j*w1[i]; //存容量
v[total]=j*v1[i]; //存价值
total++;
s1[i]-=j;
}
if(s1[i]>0)
{
w[total]=s1[i]*w1[i];
v[total]=s1[i]*v1[i];
total++;
}
}
for(int i=0;i<total;i++)
{
for(int j=m;j>=w[i];j--)
{
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
}
}
printf("%d\n",dp[m]);
return 0;
}
单调队列优化:
鸽一下233
二、混合背包问题
其实这个问题就是把多重背包转换成01背包,然后就是一个题中可以分成01背包一种情况,完全背包一种情况。
三、二维费用的背包问题
就是比01背包多了一维。
01背包:dp[i] //表示体积是i时,最大的价值
二维费用的背包问题:dp[i][j] //体积是i、重量是j时,最大的价值
只是加了一个限制条件,和01背包处理方法相同的。
#include <stdio.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=1001;
const int maxnn=101;
int main()
{
int N,V,M;
int v[maxn],m[maxn],w[maxn];
int dp[maxnn][maxnn]={0}; //dp[i]:体积是i时,最大价值是多少
//dp[i][j]:体积为i,质量为j,最大价值是多少
scanf("%d %d %d",&N,&V,&M); //容积 重量
for(int i=0;i<N;i++)
{
scanf("%d %d %d",&v[i],&m[i],&w[i]);
}
for(int i=0;i<N;i++) //枚举物品
{
for(int j=V;j>=v[i];j--) //枚举体积
{
for(int k=M;k>=m[i];k--) //枚举重量
{
dp[j][k]=max(dp[j][k],dp[j-v[i]][k-m[i]]+w[i]);
}
}
}
printf("%d\n",dp[V][M]);
return 0;
}
四、分组背包
对于决策:
01背包:每组(可以这么看成)一种物品,有两个策略,选或者不选
分组背包:每组有s[i]种物品,有s[i]+1个策略,选0个,选1个... 选s个
二维:(我还得再理解一下)
#include <stdio.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=101;
int main()
{
int N,V;
int s[maxn]; //第i组物品的个数
int v[maxn][maxn],w[maxn][maxn]; //v为体积,w为价值
int dp[maxn][maxn]; //从前i组物品中选,当前体积小于等于j的最大值
scanf("%d %d",&N,&V);
for(int i=1;i<=N;i++) //i,j不从1开始,怎么改呢
{
scanf("%d",&s[i]);
for(int j=1;j<=s[i];j++)
{
scanf("%d %d",&v[i][j],&w[i][j]);
}
}
for(int i=1;i<=N;i++)
{
for(int j=0;j<=V;j++)
{
dp[i][j]=dp[i-1][j]; //不选 不是很理解
for(int k=0;k<=s[i];k++)
{
if(j>=v[i][k])
dp[i][j]=max(dp[i][j],dp[i-1][j-v[i][k]]+w[i][k]);
}
}
}
printf("%d\n",dp[N][V]);
return 0;
}
一维:
#include <stdio.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=101;
int main()
{
int N,V;
int s[maxn];s代表第i组物品的个数
int v[maxn][maxn],w[maxn][maxn];//s代表第i组物品的个数
int dp[maxn];//只从前i组物品中选,当前体积小于等于j的最大值
scanf("%d %d",&N,&V);
for(int i=0;i<N;i++)
{
scanf("%d",&s[i]);
for(int j=0;j<s[i];j++)
{
scanf("%d %d",&v[i][j],&w[i][j]);
}
}
for(int i=0;i<N;i++) //枚举物品
{
for(int j=V;j>=0;j--) //枚举体积
{
for(int k=0;k<s[i];k++) //枚举决策
{
if(j>=v[i][k]) //保证有意义
dp[j]=max(dp[j],dp[j-v[i][k]]+w[i][k]);
}
}
}
printf("%d\n",dp[V]);
return 0;
}
还差三个。我再学学233