一、无利润背包问题+枚举法解决
题目:
小明有一只能装10千克的背包,现有白菜一颗5千克,猪肉一块2千克,酱油1.7千克,一条鱼3.5千克,白糖一袋1千克,菜油一桶5.1千克,请编写一个算法,设计小明的背包所装东西的总重量最重。
思路分析:
题目只是要求包的最终总重量最大,并没有限定选取物品的件数,但备选物品数固定,所以可以直接对这些物品进行枚举。
对于任意一种物品,只有“选”与“不选”两种情况,若“选”则该物品的重量被计入总重量中,若“不选”则不计入总重量。用变量i1、i2、i3、i4、i5、i6分别表示上面的6种物品,变量取值只有两种情况:例如对于猪肉来说,若取则i2=2,若不取则i2=0;在所有的取法中有一个限定条件:总重量w<=10,且是最大者。
main()
{
float w,bestw,i1,i2,i3,i4,i5,i6,i10,i20,i30,i40,i50,i60;
bestw=0;
for(i1=0;i1<=5;i1=i1+5)
{
for(i2=0;i2<=2;i2=i2+2)
{
for(i3=0;i3<=3.5;i3=i3+3.5)
{
for(i4=0;i4<=1.7;i4=i4+1.7)
{
for(i5=0;i5<=1;i5=i5+1)
{
for(i61=0;i6<=5.1;i6=i6+5.1)
{
w=i1+i2+i3+i4+i5+i6;
if(w<=10 and w>bestw)
{
i10=i1,120=i2,i30=i3,i40=i4,i50=i5,i60=i6;
}
print("choose:",i10,i20,i30,i40,i50,i60);
print("weight:",bestw);
}
}
}
}
}
}
}
二、无利润背包问题+回溯递归法优化
题目:
设有一个背包可以放入的物品重量为S,现有n件物品,重量分别为w1,w2……wn。问:能否从这几件物品中选择若干件放入此背包,使得放入的重量之和正好为S。如果存在一种符合上述要求的选择,则称此背包有解,否则无解。
思路分析:
此问题中,备选物品数是可知变量,所要选的物品件数也是不固定的。因此此问题没有可以用固定的(常量个)嵌套循环来枚举对象。所以在这种情况下,采用递归算法来解决。
用Knap(s,n)表示背包问题的解,这是一个布尔类型的函数,其值只能为真或假。其参数应该满足s>0,n>=1,s表示背包还可容纳物品的重量,n表示未考虑物品的件数,物品的编号为1,2,3,……n,而选取物品的尝试是从n到1进行的,这样操作便于递归。
一件物品在背包问题中只有两种可能:
(1)一种是不选择wn,这样Knsp(s,n)的解就是Knsp(s , n-1)的解;
(2)另一种是选择wn,这样Knsp(s,n)的解就是Knsp(s-wn , n-1)的解;
找到递归关系后,还有递归的停止条件:
(1)当s=0时,在尝试的路径上正好取到重量为s的物品,背包问题有解,即Knsp(0 , n)=true;
(2)当s<0时,在尝试的路径上取到的物品重量超过s,此路径上背包问题无解,即Knsp(s , n)=false;
(3)当s>0 ,但n<1时,在尝试的路径上取到的所有的物品重量达不到背包的容量,此时背包问题无解,即Knsp(s , n)=false;
float w[100];
main()
{
int n;
float s;
print("How much weight?");
input(s);
print("How many units goods?");
input(n);
for(i=1;i<=n;i++)
{
input(w[i]);
}
if(Knap(s,n)=false)
{
print("No solution!");
}
}
Knap(float s,int n) //数组w[n]为全局量
{
if(s=0)
{
return true;
}
else if(s<0 or(s>0 and n<1))
{
return false;
}
else if(Knap(s-w[n],n-1))
{
print("choose",w[n]);
return true;
}
else
{
return Knap(s,n-1);
}
}
三、与利润相关背包问题+贪心法解决
题目:
一个商人带着一个能装m千克的背包,去乡下收购货物,准备将这些货物带到城里卖掉获利。现有n种货源,且知第i种货物有wi千克,可获利pi元。编写算法帮助商人收购货物,以获取更高的利润。注意,货物不可分割、不许拆零。
思路分析:
在此题中,找出大规模与小规模问题的关系,对于每一种物品考虑仅有的两种可能“选择或者不选择”,当然选择某物品的前提是背包容量可以容纳它。
用Knap(n)表示问题的解,代表的是背包中所货物品的总利润。
当前背包的容量m大于第i件物品时,有递归关系为:Knap(m-w[i] , i-1)+p[i];
无论m是否大于第i件物品不选取第i件物品时的递归关系为:Knap(m , i-1);
Knap(n)中取较大者为当前问题的解。
float w[100],p[100],n,m;
main()
{
int s=0,i;
input(m ,n);
for(i=1;i<=n;i++)
{
input(w[i],p[i]);
s=s+w[i];
}
if(s<=m)
{
print("whole choose");
return;
}
print("max=",knap(m,n));
}
Knap(int m,int i) //数组w[n]为全局量
{
int max1,max2,t;
if(i=0)
{
return 0;
}
max1=Knap(m,i-1);
if(m>=w[i])
{
max2=Knap(m-w[i],i-1)+p[i];
}
if(max1>max2)
{
t=max1;
}
else
{
t=max2;
}
return t;
}
四、与利润相关背包问题+回溯搜索法优化
题目:
与第三题题目一致。
思路分析:
为找到最大的获利情况,先用最简单的蛮力搜索法回溯法来解决这个问题。不同于递归算法,递归算法是找大规模问题与小规模问题的关系,回溯法是对问题解空间进行搜索的算法。
(1)先确定搜索空间
n件物品的取舍数字化为:取表示为1,不取标识为0。则搜索的空间为n元一维数组(x1,x2,x3……xn),其值从(0,0,0…,0,),(0,0,0…,1,),……(1,1,1…,1,).这就是一棵子集树。
(2)确定约束条件
题目中的约束条件很明确:就是所取物品的重量和不超过m。只有取当前物品时才需要判断所取物品的重量和不超过m,如不取当前物品时,就无需进行判断,只要进一步进行深度搜索。
还有一个约束条件或者说是停止条件就是搜索完一个分支,也就是说完成一 组(x1.x2.x3…,xn)的枚举。
(3)搜索过程中的主要操作
搜索过程中一要累加所取物品的重量,回溯时还要做现场清理,也就是将当前物品置为不取状态,且从累加重量中减去当前物品的重量。
每搜索完一个分支就要计算该分支的获利情况,并记录当前的最大获利情况。
float w[100],x1[100],x[100];
main()
{
int m,n,s=0,i;
input(m ,n);
for(i=1;i<=n;i++)
{
input(w[i],p[i]);
s=s+w[i];
}
if(s<=m)
{
print("whole choose");
return;
}
Knap(1);
for(i=1;i<=n;i++)
{
print("x",i+1,"=",x[i]);
}
}
float max=0,total=0;
Knap(int i)
{
int j;
float sum=0;
if(i=n+1) //到达叶节点
{
for(j=1;j<=n;j++)
{
sum=sum+x1[j]*p[j];
}
if(sum>max)
{
max=sum;
for(j=1;j<=n;j++)
{
x[j]=x1[j];
}
}
return;
}
x1[i]=0; //不装入第i件物品的情形
Knap(i+1);
if(total+w[i]<=m) //可装入第i件物品的情形
{
x1[i]=1; //装入第i件物品的情形
total=total+w[i];
Knap(i+1);
x1[i]=0;
total=total-w[i]; //回溯之前清理现场
}
}