knapsack problems(背包问题)

时间:2021-02-12 18:42:14

背包问题

定义

我们有 n 种物品,物品 j 的重量为 wj ,价格为 pj
我们假定所有物品的重量和价格都是非负的。背包所能承受的最大重量为W。
如果限定每种物品只能选择0个或1个,则问题称为0-1背包问题
可以用公式表示为:
最大化: nj=1xjpj
受限于: nj=1xjwjW
如果限定物品j最多只能选择bj个,则问题称为有界背包问题。
可以用公式表示为:
最大化: nj=1xjpj
受限于: nj=1xjwjWxj(0,1,,bj)
如果不限定每种物品的数量,则问题称为*背包问题。
各类复杂的背包问题总可以变换为简单的0-1背包问题进行求解。

分数背包问题

定义

背包问题中的 xj 可以取的值为任意实数,包括分数,上限为物体 j 的数目。对于分数背包问题,我们采用贪心算法,每次选择余下物品中单价最大的。

例题

三种物体,价值分别是:60,100,120,重量分别是:10,20,30,背包所能承受的总重量为:50;求解最大的价值装在方案。

代码

注:1.定义了结构体用以储存价值,重量和单价
2.排序使用的快排,按照单价排序。

//============================================================================
// Name : knapsack.cpp
// Author : selous
// Version :
// Copyright : Your copyright notice
// Description : fraction knapsack problems;greedy strategy
//============================================================================

#include <iostream>
using namespace std;

const int N = 3;//types of things;

typedef struct goods{
    float weigh;//the weigh of the goods;
    float value;//the value of the goods;
    float price;//the per weigh price of goods;
}goods;

void knapsack(int knapsackWeigh,goods *g,float *x);

void Qsort(goods *g,int low,int high);

int partition(goods *g,int low,int high);

void sort(goods *g);

void print(goods *g,int low,int high);
int main() {

    float knapsackWeigh = 50;//the weigh of knapsack
    goods g[N+1]={{0,0,0},{10,60,0},{20,100,0},{30,120,0}};
    int i;
    for(i=1;i<=N;i++){
        g[i].price=g[i].value/g[i].weigh;
    }
// int weigh[N] = {10,20,30};//the weigh of each things

// int value[N] = {60,100,120};//the volume of each things
    float x[N+1]={0,0,0,0};//the num of each things in the knapsack
    knapsack(knapsackWeigh,g,x);//solve the knapsack problem
    cout<<"背包的总重量为:"<<knapsackWeigh<<endl;
    print(g,1,N);
    for(int i=1;i<=N;i++){
        //print the num of things in the knapsack;
        cout<<"第"<<i<<"个物体的个数为:"<<x[i]<<endl;
    }
    return 0;
}
void knapsack(int knapsackWeigh,goods *g,float *x){
    sort(g);
    //sort the goods according to the price;
    int i;
    for(i=1;i<=N;i++){
        if(knapsackWeigh<g[i].weigh){
            break;
        }
        x[i]+=1;
        knapsackWeigh-=g[i].weigh;
    }
    if(i<=N){
        x[i] = knapsackWeigh/g[i].weigh;
    }
}

//first:set a pivot key to divide the array.
//second:divide the array to different sub array
int partition(goods *g,int low,int high){
    g[0]=g[low];
    float pivotkey = g[low].price;
    //divide according the pivotkey
    while(low<high){

        while(low<high&&g[high].price<=pivotkey) --high;
        g[low]=g[high];
        while(low<high&&g[low].price>=pivotkey)  ++low;
        g[high]=g[low];
    }
    //recover the pivot key to the middle of the array.
    g[low]=g[0];
    return low;
}

//quick sort
void Qsort(goods *g,int low,int high){
    if(low < high){
        int pivotloc = partition(g,low,high);
        Qsort(g,low,pivotloc-1);
        Qsort(g,pivotloc+1,high);
    }
}
void sort(goods *g){
    Qsort(g,1,N);
}
void print(goods *g,int low,int high){

    cout<<"g的取值为:";
    for(int i=low;i<=high;i++){
        cout<<"第"<<i<<"个物体的属性为("<<g[i].weigh<<","
                <<g[i].value<<","<<g[i].price<<")"<<endl;
    }
}

关于贪心策略,在acm中看到一个特别好的题目。贴在下面,之后会写一篇博客贴上对应答案。过河问题:
在漆黑的夜里,N位旅行者来到了一座狭窄而且没有护栏的桥边。如果不借助手电筒的话,大家是无论如何也不敢过桥去的。不幸的是,N个人一共只带了一只手电筒,而桥窄得只够让两个人同时过。如果各自单独过桥的话,N人所需要的时间已知;而如果两人同时过桥,所需要的时间就是走得比较慢的那个人单独行动时所需的时间。问题是,如何设计一个方案,让这N人尽快过桥。
输入
第一行是一个整数T(1<=T<=20)表示测试数据的组数
每组测试数据的第一行是一个整数N(1<=N<=1000)表示共有N个人要过河
每组测试数据的第二行是N个整数Si,表示此人过河所需要花时间。
输出
输出所有人都过河需要用的最少时间
题目来源:http://acm.nyist.net/JudgeOnline/problem.php?pid=47

0/1背包问题

定义

背包问题中的 xj 取值为0或者1,对于这种典型的背包问题,采用动态规划的思想实现。
贴一个对0/1背包问题讲解比较详细的博客:http://blog.csdn.net/mu399/article/details/7722810

其中动态规划涉及的递推关系为: L[i][j]=max{L[i1][j],L[i1][jwi]+vi} 取不放入背包和放入背包情况下的value的较大值。

例题

背包容量为9,4种物体的体积为:2,3,4和5;价值分别为:3,4,5,7求使得背包价值最大的选择。

代码

//============================================================================
// Name : bknapsack.cpp
// Author : selous
// Version :
// Copyright : Your copyright notice
// Description : 0/1 knapsack problem;Dynamic programming
//============================================================================

#include <iostream>
using namespace std;

#define debug 1
const int N=4;//the numble of goods;

const int knapsackWeight=9;//the max weight of the knapsack

typedef struct goods{
    int weight;
    int value;
}goods;


int main() {

    goods g[N+1]={{0,0},{2,3},{3,4},{4,5},{5,7}};
    int value[N+1][knapsackWeight+1];

    int i,j;
    //initial the first column of the value matrix;
    for(i=0;i<N+1;i++){
        value[i][0]=0;
    }
    //initial the first row of the value matrix;
    for(j=0;j<knapsackWeight+1;j++){
        value[0][j]=0;
    }
    if(debug==0){
        for(i=0;i<N+1;i++){
            for(j=0;j<knapsackWeight;j++){
                cout<<value[i][j]<<" ";
            }
            cout<<endl;
        }
    }

    for(i=1;i<N+1;i++){
        for(j=1;j<knapsackWeight+1;j++){
// cout<<"hello"<<i<<j<<endl;
            //do not put the ith goods into the knapsack;
            value[i][j]=value[i-1][j];
            //if the ith can put into the knapsack;
            if(g[i].weight<=j){
                //put the ith goods intp the knapsack;
                int tempValue=value[i-1][j-g[i].weight]+g[i].value;
                //compare the values of put and notput
                if(value[i][j]<tempValue){
                    value[i][j]=tempValue;
                }
            }
        }
    }

    for(i=0;i<N+1;i++){
        for(j=0;j<knapsackWeight+1;j++){

            cout<<value[i][j]<<" ";
        }
        cout<<endl;
    }

    cout<<"背包最大容量为:";

    cout<<value[N][knapsackWeight];

    return 0;
}

完全背包问题

定义

背包问题中的 xi 的取值可以为所有的正整数。其中动态规划涉及的递推关系为: L[i][j]=max{L[i1][j],L[i][jwi]+vi} 取不放入背包和放入背包情况下的value的较大值。
关于该递推关系的思考:真实的递推关系应该是 L[i][j]=max{L[i][jxiwi]+xivi}0xiwij
1.当 xi=0 时,其值为 L[i1,j]
2.当 xi0 时, L[i][jwi]+viL[i][j2wi]+2vi=L[i][j2wi]+vi+vi ,其中 L[i][jwi] 为物体种类为 i ,背包大小 jwi 的最大值,故 L[i][jwi]L[i][j2wi]+vi ,
综上两点可以得到,该递推关系可以简化为 L[i][j]=max{L[i1][j],L[i][jwi]+vi}

例题

背包容量为9,4种物体的体积为:2,3,4和5;价值分别为:3,4,5,7求使得背包价值最大的选择。

代码

只需要将上题的递推关系改为:

int tempValue = valueMatrix[i][j-g[i].weight]+g[i].value;

贴一道ACM中比较好玩的关于完全背包的问题:http://ac.jobdu.com/problem.php?pid=1454

关于上面这个题目需要注意的该题是完全背包的拓展,求解是背包可容纳的最小值,所以需要将矩阵初始化为最大值,如999999,递推关系取最小值。对于算法执行后仍然为999999的,属于不可能出现的情况。

多重背包问题

对于多重背包问题,在网上没有找到特别好的思想去解决。主流方法就是将问题转化为0/1背包问题,下面这篇博客也提到了二进制转化。下次有时间试试。
http://www.cnblogs.com/tanky_woo/archive/2010/07/31/1789621.html

——2017.2.16