knapsack problems(背包问题)

来源:互联网 发布:wubi安装ubuntu的缺点 编辑:程序博客网 时间:2024/06/06 11:42

背包问题

定义

我们有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 arrayint 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 sortvoid 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 1const int N=4;//the numble of goods;const int knapsackWeight=9;//the max weight of the knapsacktypedef 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

0 0
原创粉丝点击