每件物品只能用一次
#include
using namespace std;
const int MAXN = 1005;
int v[MAXN]; // 体积
int w[MAXN]; // 价值
int f[MAXN][MAXN]; // f[i][j], j体积下前i个物品的最大价值
int main()
{
int n, m; //物品数量、背包容积
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; i++)
scanf("%d%d",&v[i],&w[i]);//给每个物品赋大小和价值
for(int i = 1; i <= n; i++) //i是当前物品,一共n个物品
for(int j = 0; j <= m; j++)//j为当前剩余容积,一定要小于一共的容积,当前剩余容积从0开始,每次+1,直到有m容积
{
//当前容量装不进第i个物品,则当前容积的价值等于前i-1个物品
if(j < v[i])
f[i][j] = f[i - 1][j];
// 能装,需进行判断是否选择第i个物品
else //判断方法:不要i物品或要i物品,但是要i的话就看看容量-i的体积,剩下的容量装了多少价值。再比较是否要i
f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i]);
}
printf("%d\n",f[n][m]);
return 0;
}
#include
using namespace std;
const int MAXN = 1005;
int v[MAXN]; // 体积
int w[MAXN]; // 价值
int f[MAXN]; // f[j],背包容量j下的最优解。
int main()
{
int n, m; //物品数量、背包容积
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; i++)
scanf("%d%d",&v[i],&w[i]);//给每个物品赋大小和价值
for(int i = 1; i <= n; i++) //i是当前物品,一共n个物品
for(int j = m; j >=v[i]; j--)//j为当前剩余容积,一定要大于要操作的物品的大小,当前剩余容积从m开始,每次-1
{
//当前容量装不进第i个物品,则当前容积的价值等于前i-1个物品,则不会进入循环
// 能装,需进行判断是否选择第i个物品
//判断方法:遍历到前i-1个物品的j的容量保存的价值 比较 遍历到前i-1个物品的j-v[i]的容量加i的价值
//不要i物品或要i物品,但是要i的话就看看容量-i的体积,剩下的容量装了多少价值。再比较是否要i
//为什么一维情况下枚举背包容量需要逆序?
//在二维情况下,状态f[i][j]是由上一轮i - 1的状态得来的,f[i][j]与f[i - 1][j]是独立的。
//而优化到一维后,如果我们还是正序,则有f[较小体积]更新到f[较大体积],则有可能本应该用第i-1轮的状态却用的是第i轮的状态。
//例如,一维状态第i轮对体积为 3 的物品进行决策,则f[7]由f[4]更新而来,这里的f[4]正确应该是f[i - 1][4],但从小到大枚举j这里的f[4]在第i轮计算却变成了f[i][4]。
//当逆序枚举背包容量j时,我们求f[7]同样由f[4]更新,但由于是逆序,从f[7]更新时用的到f[4]还未更新,这里的f[4]还没有在第i轮计算,所以此时实际计算的f[4]仍然是f[i - 1][4]。
f[j] = max(f[j], f[j - v[i]] + w[i]);
}
printf("%d\n",f[m]);
return 0;
}
每件物品可以用无限次
#include
using namespace std;
const int N = 1010;
int f[N][N];// f[i][j], j体积下前i个物品的最大价值
int v[N];// 体积
int w[N];// 价值
int main()
{
int n,m;
scanf("%d%d",&n,&m);//物品数量、背包容积
for(int i = 1 ; i <= n ;i ++)//i是当前物品,一共n个物品
{
scanf("%d%d",&v[i],&w[i]);//给每个物品赋大小和价值
}
for(int i = 1 ; i<=n ;i++)
for(int j = 0 ; j<=m ;j++)//j为当前剩余容积,一定要小于一共的容积,当前剩余容积从0开始,每次+1
{
//当前容量装不进第i个物品,则当前容积的价值等于前i-1个物品
if(j < v[i])
f[i][j] = f[i - 1][j];
else//是否选择该物品
//不要i物品或要i物品,但是要i的话就看看容量-i的体积,剩下的容量装了多少价值,再比较是否要i
//不用i则用i-1的状态,用i则用i的状态,因为i可以无限使用
f[i][j] = max(f[i - 1][j],f[i][j - v[i]] + w[i]);
}
printf("%d\n",f[n][m]);
return 0;
}
#include
using namespace std;
const int N = 1010;
int f[N];// f[j], j体积下前i个物品的最大价值
int v[N];// 体积
int w[N];// 价值
int main()
{
int n,m;
scanf("%d%d",&n,&m);//物品数量、背包容积
for(int i = 1 ; i <= n ;i ++)//i是当前物品,一共n个物品
{
scanf("%d%d",&v[i],&w[i]);//给每个物品赋大小和价值
}
for(int i = 1 ; i<=n ;i++)
//这里用v[i]的体积作为初始体积,也就是说不看i前面的物品了,只看i后面的物品。但i是从第一个开始到第n个的,所以不会有问题
for(int j = v[i] ; j<=m ;j++)//f为i物品的体积注意了,这里的j是从小到大枚举,和01背包不一样
{
//当前容量装不进第i个物品,则当前容积的价值等于前i-1个物品,则不会进入循环
// 能装,需进行判断是否选择第i个物品
//遍历到前i个物品的j的容量保存的价值 比较 遍历到前i-1个物品的j-k*v[i]的容量加k*i的价值
//注意是遍历到i-1个物品,因为可能装不进,所以j-k*v[i]的容量不一定是i-1的价值
//若i-1的物品的大小大于i物品,且价值小于i,所以后者更小
//若i-1的物品的大小小于i物品,且价值大于i,所以后者更大
f[j] = max(f[j],f[j-v[i]]+w[i]);
}
printf("%d\n",f[m]);
return 0;
}
每个物品有不同的次数限制
#include
#include
using namespace std;
const int N = 110;
int v[N];// 体积
int w[N];// 价值
int s[N];// 该物品的次数
int f[N][N];// f[i][j], j体积下前i个物品的最大价值
int n, m;
int main()
{
scanf("%d%d",&n,&m);//物品数量、背包容积
for(int i = 1; i <= n; i ++) //i是当前物品,一共n个物品
scanf("%d%d%d",&v[i],&w[i],&s[i]);//给每个物品赋大小和价值和次数
for(int i = 1; i <= n; i ++)
{
for(int j = 0; j <= m; j ++)//j为当前剩余容积,一定要小于一共的容积,当前剩余容积从0开始,每次+1
{
for(int k = 0; k <= s[i]&&k*v[i]<=j; k ++)//该物品放入k次,直到容量不够放或达到次数限制
{
//遍历到前i个物品的j的容量保存的价值 比较 遍历到前i-1个物品的j-k*v[i]的容量加k*i的价值
//注意是遍历到i-1个物品,因为可能装不进,所以j-k*v[i]的容量不一定是i-1的价值
//若i-1的物品的大小大于i物品,且价值小于i,所以后者更小
//若i-1的物品的大小小于i物品,且价值大于i,所以后者更大
//比较当前的价值和上一物品的价值加k次当前物品的价值
f[i][j] = max(f[i][j], f[i - 1][j - k * v[i]] + k * w[i]);
}
}
}
printf("%d\n",f[n][m]);
return 0;
}
#include
using namespace std;
const int N = 12010, M = 2010;
int n, m;
int v[N];// 体积
int w[N];// 价值
int f[M]; // f[j], j体积下前i个物品的最大价值
int main()
{
scanf("%d%d",&n,&m);//物品数量、背包容积
int cnt = 0; //分组的组别
for(int i = 1;i <= n;i ++)
{
int a,b,s;
scanf("%d%d%d",&a,&b,&s);//第 i 种物品的体积、价值和数量。
int k = 1; // 组别里面的个数
while(k<=s)//组别里的个数要小于等于它有的数量
{
cnt ++ ; //组别先增加:1 2 4 8 16 32 64 128...
v[cnt] = a * k ; //整体体积:该物品的体积*个数
w[cnt] = b * k; // 整体价值:该物品的价值*个数
s -= k; // s要减小,使用了k个,还剩多少,这个是分组的内容:7可以分成1+2+4,所以这里s=7,6,4,0
k *= 2; // 组别里的个数增加,因为是2指数增长k:1 2 4 8(没有到8就直接跳出),k=4时 s=4.这次后直接跳出循环
}
//剩余的一组
if(s>0)//此时若s还未到0,则说明还有一个数c(第一张图中的c)
{
cnt ++ ;
v[cnt] = a*s;//该物品的体积*该物品剩余的数量
w[cnt] = b*s;//该物品的价值*该物品剩余的数量
}
}
n = cnt ; //枚举次数正式由个数变成组别数,一共就只用访问n次就行了,比如7就只用访问3次
//01背包一维优化
for(int i = 1; i <= n; i++) //i是当前物品,一共n个物品
for(int j = m; j >=v[i]; j--)//j为当前剩余容积,一定要大于要操作的物品的大小,当前剩余容积从m开始,每次-1
{
//当前容量装不进第i个物品,则当前容积的价值等于前i-1个物品,则不会进入循环
// 能装,需进行判断是否选择第i个物品
//判断方法:遍历到前i-1个物品的j的容量保存的价值 比较 遍历到前i-1个物品的j-v[i]的容量加i的价值
//注意是遍历到i-1个物品,因为可能装不进,所以j-v[i]的容量不一定是i-1的价值
//为什么一维情况下枚举背包容量需要逆序?
//在二维情况下,状态f[i][j]是由上一轮i - 1的状态得来的,f[i][j]与f[i - 1][j]是独立的。
//而优化到一维后,如果我们还是正序,则有f[较小体积]更新到f[较大体积],则有可能本应该用第i-1轮的状态却用的是第i轮的状态。
//例如,一维状态第i轮对体积为 3 的物品进行决策,则f[7]由f[4]更新而来,这里的f[4]正确应该是f[i - 1][4],但从小到大枚举j这里的f[4]在第i轮计算却变成了f[i][4]。
//当逆序枚举背包容量j时,我们求f[7]同样由f[4]更新,但由于是逆序,从f[7]更新时用的到f[4]还未更新,这里的f[4]还没有在第i轮计算,所以此时实际计算的f[4]仍然是f[i - 1][4]。
//此时的w存的就是组别的价值,而非某个物品的价值
f[j] = max(f[j], f[j - v[i]] + w[i]);
}
printf("%d\n",f[m]);
return 0;
}
每组有不同的体积和价值的物品,每组只能选一个物品
#include
using namespace std;
const int N=110;
int f[N];// f[j], j体积下前i个物品的最大价值
int v[N][N]; // 体积
int w[N][N]; // 价值
int s[N];//i组内的物品
int n,m,k;
int main()
{
scanf("%d%d",&n,&m);//物品数量、背包容积
for(int i=1;i<=n;i++)
{
scanf("%d",&s[i]);//第i组
for(int j=1;j<=s[i];j++)
{
scanf("%d%d",&v[i][j],&w[i][j]);//i组内的j物品的体积和价值
}
}
for(int i=1;i<=n;i++)
{
for(int j=m;j>=0;j--)//让体积一开始就是m,每次都让体积减小1
{
for(int k=1;k<=s[i];k++)//遍历组内物品
{
if(j>=v[i][k])//若剩余体积大于该物品体积,判断是否加入ik物品
//不要ik物品或要ik物品,但是要ik的话就看看容量-ik的体积,剩下的容量装了多少价值。再比较是否要ik
f[j]=max(f[j],f[j-v[i][k]]+w[i][k]);
}
}
}
printf("%d\n",f[m]);
}