0x1 题目叙述
有n件物品和一个容量为c的背包。第i件物品的价值是v[i],重量是w[i]。求解将哪些物品装入背包可使价值总和最大。所谓01背包,表示每一个物品只有一个,要么装入,要么不装入。
输入:
第1行是背包容量
第2行物品个数n
第3行:n个物品的重量
第4行:n个物品的价值
输出:
第1行:最优值
第2行:最优解
输入输出范例:
输入:
50 //背包容量
4 //物品个数
30 20 40 10 //物品重量
10 20 30 40 //物品价值
输出:
70 //最优值
0 0 1 1 //最优解
该问题的约束函数是什么?
约束函数为:
∑wixi≤C,即背包能装下物品
0x2 代码
语言:C++
注意:下面的代码使用double型变量
考虑约束函数,不考虑限界函数
#include <iostream>
#include <stdio.h>
using namespace std;
int n;//物品数量
double c;//背包容量
double v[100];//各个物品的价值 value
double w[100];//各个物品的重量 weight
double cw = 0.0;//当前背包重量 current weight
double cp = 0.0;//当前背包中物品总价值 current value
double bestp = 0.0;//当前最优价值best price
double perp[100];//单位物品价值(排序后) per price
int order[100];//物品编号
int put[100];//设置是否装入,为1的时候表示选择该组数据装入,为0的表示不选择该组数据
int bestput[100];//最优解 0 1
//回溯函数
void backtrack(int i)
{ //i用来指示到达的层数(第几步,从0开始),同时也指示当前选择完了几个物品
double bound(int i);
if(i>n) //递归结束的判定条件
{
if(cp>bestp)
{
bestp = cp;
for(int i=1;i<=n;i++)
{
bestput[i]= put[i];
}
}
return;
}
//如若左子节点可行,则直接搜索左子树;
if(cw+w[i]<=c)//将物品i放入背包,搜索左子树
{
cw+=w[i];//同步更新当前背包的重量
cp+=v[i];//同步更新当前背包的总价值
put[i]=1;
backtrack(i+1);//深度搜索进入下一层
cw-=w[i];//回溯复原
cp-=v[i];//回溯复原
}
//右子树
put[i]=0;
backtrack(i+1);
}
int main()
{
int i;
for(i=0;i<100;i++)
put[i]=0;
printf("请输入物品的数量和背包的容量:");
scanf("%d %lf",&n,&c);
printf("请依次输入%d个物品的重量:\n",n);
for(i=1;i<=n;i++){
scanf("%lf",&w[i]);
order[i]=i;
}
printf("请依次输入%d个物品的价值:\n",n);
for(i=1;i<=n;i++){
scanf("%lf",&v[i]);
}
backtrack(1);
printf("最优价值为:%lf\n",bestp);
printf("需要装入的物品编号是:");
for(i=1;i<=n;i++)
{
if(bestput[i]==1)
printf("%d ",order[i]);
}
return 0;
}
/*
test data
4 50
30 20 40 10
10 20 30 40
*/
考虑约束函数,考虑限界函数
#include <iostream>
#include <stdio.h>
using namespace std;
int n;//物品数量
double c;//背包容量
double v[100];//各个物品的价值 value
double w[100];//各个物品的重量 weight
double cw = 0.0;//当前背包重量 current weight
double cp = 0.0;//当前背包中物品总价值 current value
double bestp = 0.0;//当前最优价值best price
double perp[100];//单位物品价值(排序后) per price
int order[100];//物品编号
int put[100];//设置是否装入,为1的时候表示选择该组数据装入,为0的表示不选择该组数据
int bestput[100];//最优解 0 1
//回溯函数
void backtrack(int i)
{ //i用来指示到达的层数(第几步,从0开始),同时也指示当前选择完了几个物品
double bound(int i);
if(i>n) //递归结束的判定条件
{
if(cp>bestp)
{
bestp = cp;
for(int i=1;i<=n;i++)
{
bestput[i]= put[i];
}
}
return;
}
//如若左子节点可行,则直接搜索左子树;
//对于右子树,先计算上界函数,以判断是否将其减去
if(cw+w[i]<=c)//将物品i放入背包,搜索左子树
{
cw+=w[i];//同步更新当前背包的重量
cp+=v[i];//同步更新当前背包的总价值
put[i]=1;
backtrack(i+1);//深度搜索进入下一层
cw-=w[i];//回溯复原
cp-=v[i];//回溯复原
}
if(bound(i+1)>bestp)//如若符合条件则搜索右子树
{
put[i]=0;
backtrack(i+1);
}
}
//计算上界函数,功能为剪枝
double bound(int i)
{ //判断当前背包的总价值cp+剩余容量可容纳的最大价值<=当前最优价值
double leftw= c-cw;//剩余背包容量
double b = cp;//记录当前背包的总价值cp,最后求上界
//以物品单位重量价值递减次序装入物品
while(i<=n && w[i]<=leftw)
{
leftw-=w[i];
b+=v[i];
i++;
}
//装满背包
if(i<=n)
b+=v[i]/w[i]*leftw;
return b;//返回计算出的上界
}
int main()
{
int i;
for(i=0;i<100;i++)
put[i]=0;
printf("请输入物品的数量和背包的容量:");
scanf("%d %lf",&n,&c);
printf("请依次输入%d个物品的重量:\n",n);
for(i=1;i<=n;i++){
scanf("%lf",&w[i]);
order[i]=i;
}
printf("请依次输入%d个物品的价值:\n",n);
for(i=1;i<=n;i++){
scanf("%lf",&v[i]);
}
backtrack(1);
printf("最优价值为:%lf\n",bestp);
printf("需要装入的物品编号是:");
for(i=1;i<=n;i++)
{
if(put[i]==1)
printf("%d ",order[i]);
}
return 0;
}
/*
test data
4 50
30 20 40 10
10 20 30 40
*/
考虑约束函数,考虑限界函数,排序
#include <iostream>
#include <stdio.h>
using namespace std;
int n;//物品数量
double c;//背包容量
double v[100];//各个物品的价值 value
double w[100];//各个物品的重量 weight
double cw = 0.0;//当前背包重量 current weight
double cp = 0.0;//当前背包中物品总价值 current value
double bestp = 0.0;//当前最优价值best price
double perp[100];//单位物品价值(排序后) per price
int order[100];//物品编号
int put[100];//设置是否装入,为1的时候表示选择该组数据装入,为0的表示不选择该组数据
int bestput[100];//最优解 0 1
//按单位价值排序
void knapsack()
{
int i,j;
int temporder = 0;
double temp = 0.0;
for(i=1;i<=n;i++)
perp[i]=v[i]/w[i]; //计算单位价值(单位重量的物品价值)
for(i=1;i<=n-1;i++)
{
for(j=i+1;j<=n;j++)
if(perp[i]<perp[j])//冒泡排序perp[],order[],sortv[],sortw[]
{
temp = perp[i]; //冒泡对perp[]排序
perp[i]=perp[i];
perp[j]=temp;
temporder=order[i];//冒泡对order[]排序
order[i]=order[j];
order[j]=temporder;
temp = v[i];//冒泡对v[]排序
v[i]=v[j];
v[j]=temp;
temp=w[i];//冒泡对w[]排序
w[i]=w[j];
w[j]=temp;
}
}
}
//回溯函数
void backtrack(int i)
{ //i用来指示到达的层数(第几步,从0开始),同时也指示当前选择完了几个物品
double bound(int i);
if(i>n) //递归结束的判定条件
{
if(cp>bestp)
{
bestp = cp;
for(int i=1;i<=n;i++)
{
bestput[i]= put[i];
}
}
return;
}
//如若左子节点可行,则直接搜索左子树;
if(cw+w[i]<=c)//将物品i放入背包,搜索左子树
{
cw+=w[i];//同步更新当前背包的重量
cp+=v[i];//同步更新当前背包的总价值
put[i]=1;
backtrack(i+1);//深度搜索进入下一层
cw-=w[i];//回溯复原
cp-=v[i];//回溯复原
}
if(bound(i+1)>bestp)//如若符合条件则搜索右子树
{
put[i]=0;
backtrack(i+1);
}
}
//计算上界函数,功能为剪枝
double bound(int i)
{ //判断当前背包的总价值cp+剩余容量可容纳的最大价值<=当前最优价值
double leftw= c-cw;//剩余背包容量
double b = cp;//记录当前背包的总价值cp,最后求上界
//以物品单位重量价值递减次序装入物品
while(i<=n && w[i]<=leftw)
{
leftw-=w[i];
b+=v[i];
i++;
}
//装满背包
if(i<=n)
b+=v[i]/w[i]*leftw;
return b;//返回计算出的上界
}
int main()
{
int i;
for(i=0;i<100;i++)
put[i]=0;
printf("请输入物品的数量和背包的容量:");
scanf("%d %lf",&n,&c);
printf("请依次输入%d个物品的重量:\n",n);
for(i=1;i<=n;i++){
scanf("%lf",&w[i]);
order[i]=i;
}
printf("请依次输入%d个物品的价值:\n",n);
for(i=1;i<=n;i++){
scanf("%lf",&v[i]);
}
knapsack();
backtrack(1);
printf("最优价值为:%lf\n",bestp);
printf("需要装入的物品编号是:");
for(i=1;i<=n;i++)
{
if(bestput[i]==1)
printf("%d ",order[i]);
}
return 0;
}
/*
test data
4 50
30 20 40 10
10 20 30 40
*/