问题描述:
有n件物品(是件不是种!),每件物品有自己的重量w[n]和价值v[n];现有一个容量为bag的背包,要在背包容量满足的条件下将物品们装入背包,使总价值最大,试问应该怎样放入?最大值为多少?
基本思路:
该问题中每个物体仅有放入或不放入两种情况,故称为01背包问题。
现有二元函数maxvalue(i,wight) 表示在考虑前i件物体的情况下,容量为wight的背包所能达到的最大价值。
考虑是否放入第i个物体:
若不放入,则有maxvalue(i,wight)=maxvalue(i-1,wight) ;
若放入,则有maxvalue(i,wight)= maxvalue(i-1,wight-w[i])+ v[i] 。
故该函数满足以下递归式:
maxvalue(i,wight)=max { maxvalue(i-1, wight) , maxvalue(i-1,wight-w[i])+v[i] }
建立二维数组maxvalue[n+1][wight+1],由动态规划打表即可求出每个状态下的maxvalue值,其中maxvalue[n+1][wight+1]的值即为所求。再通过对该值进行递归回溯,即可找到满足该值的物品组成。
下面是代码
#include
int max(int a, int b)
{
return a > b ? a : b;
}
int main()
{
int n, bag;//n为物体个数,bag为背包容量
printf("\n请输入物体个数和背包容量:\n");
scanf("%d %d",&n,&bag);
int w[100];//每个物体重量
int v[100];//每个物体价值
printf("请依次输入每个物体的重量和价值:\n");
for(int i = 1; i <= n; i++)
{
scanf("%d %d", &w[i], &v[i]);
}
int maxvalue[100][100] = {0};//动态规划表
//制表
for (int i = 1; i <= n; i++)
{
for (int wight = 1; wight <= bag; wight++)
{
if (wight < w[i])
maxvalue[i][wight] = maxvalue[i-1][wight];
else
maxvalue[i][wight] = max(maxvalue[i-1][wight], maxvalue[i-1][wight-w[i]] + v[i]);
}
}
//打表
printf("w i g h t: ");
for(int wight=1; wight <= bag; wight++) printf("%-3d", wight);
printf("\n");
for(int i = 1; i <= n; i++)
{
printf("w%d,v%-3di=%d ", w[i],v[i], i);
for(int wight = 1; wight <= bag; wight++)
{
printf("%-3d", maxvalue[i][wight]);
}
printf("\n");
}
printf("\n");
//输出最大值
printf("MAX=%d\n", maxvalue[n][bag]);
//最大值回溯
int judge[100] = {0};
int x = n, y = bag;
while(x != 0 && y != 0)
{
if(maxvalue[x][y] != maxvalue[x-1][y])
{
judge[x]++;
y-=w[x];
x--;
}
else x--;
}
//输出满足条件的物体
for(int i = 1; i <= n; i++)
{
if(judge[i]) printf("NO.%d wight%d value%d\n", i, w[i], v[i]);
}
}
问题描述:
在01背包的基础上,现在每件物体的数量不再为1,而是各自给定,依然求满足题意的最大总价值。
基本思路:
很容易想到,我们可以模仿01背包的做法,将n件某种物体分为n个1件同种物体,再01背包一下。这样就会增加n件物体。为了减少增加的物体个数,我们可以利用二进制优化。
有引理:
n以内的正整数,都可以用1, 2, 4,…, 2(k-1), n-2k+1中任意几个数之和表示
于是,对于n件某种物体,我们便可以增加k个物体,每个物体的重量和价值分别为原物体的1, 2, 4,…, 2(k-1), n-2k+1倍,这样便可以模拟该种物体放0~n个的所有情况。
#include
#include
int max(int a, int b)
{
return a > b ? a : b;
}
int main()
{
int n, bag;
scanf("%d %d",&n,&bag); //物体种数,背包容量
int w[110], v[110], num[110];
//转化为多重背包
int w1[5000];
int v1[5000];
for(int i = 1; i <= n; i++)
{
scanf("%d %d %d", &w[i], &v[i], &num[i]); //重量,价值,数量
}
int dp[10000];
int index = 0;
for(int i = 1;i <= n;i++)
{
int t = num[i], k = 1;
while(t - k > 0)
{
t -= k;
w1[++index] = k * w[i];
v1[index] = k * v[i];
}
w1[++index] = t * w[i];
v1[index] = t * v[i];
}
memset(dp,0,sizeof(dp));
for(int i = 1;i <= index;i++)
for(int j = bag;j >= w1[i];j--)
dp[j] = max(dp[j],dp[j - w1[i]] + v1[i]);
printf("%d\n",dp[bag]);
}