1004.0-1背包问题
时限:1000ms 内存限制:10000K 总时限:3000ms
描述
需对容量为c 的背包进行装载。从n 个物品中选取装入背包的物品,每件物品i 的重量为wi ,价值为pi 。对于可行的背包装载,背包中物品的总重量不能超过背包的容量,最佳装载是指所装入的物品价值最高。
输入
多个测例,每个测例的输入占三行。第一行两个整数:n(n<=10)和c,第二行n个整数分别是w1到wn,第三行n个整数分别是p1到pn。
n 和 c 都等于零标志输入结束。
输出
每个测例的输出占一行,输出一个整数,即最佳装载的总价值。
输入样例
1 2
1
1
2 3
2 2
3 4
0 0
输出样例
1
4
解析:01背包问题有很多种解法,这里介绍两种。
第一种:回溯法。回溯法使用深度优先的搜索方式,搜索了01背包问题的解空间树的每一个节点,最后得出最优解。
#include<stdio.h>
#include<string.h>
int n,c;
int bestv=0,cw,cv;
int w[11],v[11];
void backpack(int cw,int cv,int i)//分别表示当前质量,当前价值,第i件东西
{
if(i>n)//这里是容易犯错的地方,如果你是从0开始存的,那么这里的判断条件应该是i>=n或i>n-1
{
if(bestv<cv)
bestv=cv;
}
else
{
if(w[i]+cw<=c)
backpack(cw+w[i],cv+v[i],i+1);//如果能装下,就装,如果装不下,就直接不装,如果能装下,也可以不装
backpack(cw,cv,i+1);
}
}
int main()
{
while(scanf(“%d%d”,&n,&c)&&(n||c))
{
memset(w,0,sizeof(w));
memset(v,0,sizeof(v));
bestv=0;cv=0;cw=0;//每次重新开始一组数时,数组和这些值都要重置为0
for(int i=1;i<=n;i++)
scanf(“%d”,&w[i]);
for(int i=1;i<=n;i++)
scanf(“%d”,&v[i]);
backpack(0,0,1);
printf(“%d\n”,bestv);
}
return 0;
}
第二种:动态规划法,动态规划法采用从底向上的方法,当前的每一步都会被记录下来,因为每一步都会对以后的结果产生影响,所以能用动态规划法做的题一般都能画出来一各表,储存了每一步的结果等待后面调用。例如这个0 1背包问题,就是画一个n*c的表格,具体实现如下:
#include<stdio.h>
#include<string.h>
int n,c;
int w[11],v[11];
int m[11][101];
int max(int a,int b)
{
if(a>b)
return a;
else
return b;
}
void package()
{
for(int i=0;i<=n;i++)
for(int j=0;j<=c;j++)
m[i][j]=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=c;j++)
{
if(j>=w[i])
m[i][j]=max(m[i-1][j-w[i]]+v[i],m[i-1][j]);//如果能装下,就选择装或者不装,看谁产生的最终价值大,最终价值一直要到最 //后一步才能确定下来
else
m[i][j]=m[i-1][j];
}
}
printf(“%d\n”,m[n][c]);
}
int main()
{
while(scanf(“%d%d”,&n,&c)&&(n||c))
{
memset(w,0,sizeof(w));
memset(v,0,sizeof(v));
memset(m,0,sizeof(m));
for(int i=1;i<=n;i++)
scanf(“%d”,&w[i]);
for(int i=1;i<=n;i++)
scanf(“%d”,&v[i]);
package();
}
return 0;
}
注:动态规划可以解决01背包问题是因为其具有最优子结构性质,即最终的最优解所包含的子问题的解也是最优的,比如在这个所得的表格中,m[n][c]是容量为c时的最优解,m[n][c-1]是容量为c-1时的最优解,以此类推。最优子结构性质是一个问题可以用动态规划法所解决的必要条件,最优子结构性质导致问题所有子问题的解可以存储在一个表格里,以备后续调用,这也引出了动态规划法另一个基本性质,即子问题重叠性质,这也是动态规划从根本上高于回溯法的重要原因。