背包问题

一、无利润背包问题+枚举法解决

题目:
小明有一只能装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]; //回溯之前清理现场
    }
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值