背包问题

背包问题:

  • 已知有n种物品,一个可容纳M重量的背包,物品i(1<=i<=n)的重量wi,对应的价值为pi。怎样的装包,才能使包中物品的总价值最大?

  • 已知条件:n,M,(p1,p2,…,pn),(w1,w2,…,wn)。

背包问题分为2种:如果物品可以拆分,就是普通背包问题。如果每个物品不能拆分,就是01背包问题。

普通背包问题-贪心算法

由于普通背包问题,物品可以拆分,假设物品i装进背包的部分的x[i],其中0<=xi<=1。思想为:1.按效益值pi/wi比值的降序排列;2.按排序次序将物品放入背包,如果剩余容量能放下物品w[i],则xi=1,能放下部分,则xi=leftM/wi,否则xi=0。
最终n1wixi=Mn1pixi

例如:n=3,M=20,(p1,p2,p3)=(25,24,15),(w1,w2,w3)=(18,15,10)。

解:按pi/wi排序,物品排序为(物品2,物品3,物品1),装包过程x2=1,x3=1/2,x2=0。结果(x1,x2,x3)=(0,1,1/2),最大效益值为31.5。

01背包问题-动态规划

对于0/1背包问题,可以通过作出变量(x1,x2,...,xi)的一个决策序列fi(x)来得到它的解。而对于变量x的决策就是决定它取0还是1。

如果设fj(x)是KNAP(1,j,x)最优值的解,那么,fn(M)就可以表示为

fn(M)max{fn1(M)fn1(Mwn)+pn}

对于任意的fi(x),这里i>0,则有
fi(x)max{fi1(x)fi1(xwi)+pi}

为了能尤前向后递推最后求解fn(M),需从f0(x)开始。对于x>=0,f0(x)=0,当x<0,f0(x)=-

其实,如果画图,可以看出每个fi都是由一些序偶(pj,wj)组成的集合所说明,其中wj是使fi在某处产生一次阶跃的x值,pj=fi(wj)。第一对序偶是(p0,w0)

算法思想:

  • S0={ (0,0) }。
  • Si1fi1的所有序偶的集合,Si1fi1(xwi)+pi的所有序偶的集合。求Si1,就是把序偶(pi,wi)加到Si1中每一对序偶,
    Si1=(p,w)|(ppi,wwi)Si1
  • 上面第二个公式中取max值,其实就是将Si1Si1归并得到Si。归并过程中,如果Si1Si1之一有序偶(pj,wj),另一个序偶(pk,wk),并且在wj>=wk的同时有pj<=pk,那么序偶(pj,wj)被舍弃。

算法时间复杂度为O(n),空间复杂度为O(n)。

例如:n=3,M=6,(p1,p2,p3)=(1,2,5),(w1,w2,w3)=(2,3,4)。

解:
S0={(0,0)}
- S11={(1,2)}
S1={(0,0),(1,2)}
- S21={(2,3),(3,5)}
S2={(0,0),(1,2),(2,3),(3,5)}
- S31={(5,4),(6,6),(7,7),(8,9)}
S3={(0,0),(1,2),(2,3),(3,5),(6,6),(7,7),(8,9)}
根据支配规则,在S3中删去了序偶(3,5)。

可知,f(6)=6。

01背包问题代码

//保存成KNAP.h头文件
//然后在项目主函数,main中KNAP::Test::main();调用即可
#include <iostream>
#include <vector>

using namespace std;

namespace KNAP{
    struct PWPoint{
        int p;
        int w;
        PWPoint(int _p,int _w):p(_p),w(_w){ }
    };

    class Solution{
    public:
        int knap(int n,int M,vector<int> p,vector<int> w){
            vector<PWPoint > fn,fn1;
            fn.push_back(PWPoint(0,0));
            int j,k;
            for(int i=0;i<n;i++){
                //get s1(i)
                fn1.clear();
                for(j=0;j<fn.size();j++){
                    fn1.push_back(PWPoint(fn[j].p+p[i],fn[j].w+w[i]));
                }

                //raw merger s(i-1) and s1(i) to s(i)
                j=0,k=0;
                while(j<fn.size() && k<fn1.size()){
                    if(fn[j].w>fn1[k].w){
                        fn.insert(fn.begin()+j,fn1[k]);
                        j++;
                        k++;
                    }else{
                        j++;
                    }
                }
                while(k<fn1.size()){
                    fn.push_back(fn1[k++]);
                }

                //delete except in s(i)
                j=0;
                while(j<fn.size()){
                    if(j<fn.size()-1){
                        if(fn[j].w == fn[j+1].w){
                            if(fn[j].p>=fn[j+1].p){
                                fn.erase(fn.begin()+j+1);
                            }else{
                                fn.erase(fn.begin()+j);
                            }
                        }else{
                            if(fn[j].p>=fn[j+1].p){
                                fn.erase(fn.begin()+j+1);
                            }else{
                                j++;
                            }
                        }
                    }else{
                        j++;
                    }
                }

                //delete fn[i] if fn[i].w>M
            }

            //get map_p for M
            int cur_p=fn[0].p;
            for(int i=1;i<fn.size();i++){
                if(M<fn[i].w){
                    break;
                }else{
                    cur_p=fn[i].p;
                }
            }

            return cur_p;

        }
    };

    class Test{
    public:
        static void main(){
            int n=3,M=6;
            vector<int> p{1,2,5},w{2,3,4};
            Solution s;
            int ret = s.knap(n, M, p, w);
            cout<< ret <<endl;
        }
    };
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值