天天看點

knapsack problems(背包問題)背包問題分數背包問題0/1背包問題完全背包問題多重背包問題

背包問題

定義

我們有 n 種物品,物品j的重量為 wj ,價格為 pj 。

我們假定所有物品的重量和價格都是非負的。背包所能承受的最大重量為W。

如果限定每種物品隻能選擇0個或1個,則問題稱為0-1背包問題

可以用公式表示為:

最大化: ∑nj=1xjpj

受限于: ∑nj=1xjwj≤W

如果限定物品j最多隻能選擇bj個,則問題稱為有界背包問題。

可以用公式表示為:

最大化: ∑nj=1xjpj

受限于: ∑nj=1xjwj≤Wxj∈(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 = ;//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 = ;//the weigh of knapsack
    goods g[N+]={{,,},{,,},{,,},{,,}};
    int i;
    for(i=;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+]={,,,};//the num of each things in the knapsack
    knapsack(knapsackWeigh,g,x);//solve the knapsack problem
    cout<<"背包的總重量為:"<<knapsackWeigh<<endl;
    print(g,,N);
    for(int i=;i<=N;i++){
        //print the num of things in the knapsack;
        cout<<"第"<<i<<"個物體的個數為:"<<x[i]<<endl;
    }
    return ;
}
void knapsack(int knapsackWeigh,goods *g,float *x){
    sort(g);
    //sort the goods according to the price;
    int i;
    for(i=;i<=N;i++){
        if(knapsackWeigh<g[i].weigh){
            break;
        }
        x[i]+=;
        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[]=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[];
    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-);
        Qsort(g,pivotloc+,high);
    }
}
void sort(goods *g){
    Qsort(g,,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[i−1][j],L[i−1][j−wi]+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=;//the numble of goods;

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

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


int main() {

    goods g[N+]={{,},{,},{,},{,},{,}};
    int value[N+][knapsackWeight+];

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

    for(i=;i<N+;i++){
        for(j=;j<knapsackWeight+;j++){
//          cout<<"hello"<<i<<j<<endl;
            //do not put the ith goods into the knapsack;
            value[i][j]=value[i-][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-][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=;i<N+;i++){
        for(j=;j<knapsackWeight+;j++){

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

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

    cout<<value[N][knapsackWeight];

    return ;
}
           

完全背包問題

定義

背包問題中的 xi 的取值可以為所有的正整數。其中動态規劃涉及的遞推關系為: L[i][j]=max{L[i−1][j],L[i][j−wi]+vi} 取不放入背包和放入背包情況下的value的較大值。

關于該遞推關系的思考:真實的遞推關系應該是 L[i][j]=max{L[i][j−xi∗wi]+xi∗vi}0≤xi∗wi≤j

1.當 xi=0 時,其值為 L[i−1,j] 。

2.當 xi≠0 時, L[i][j−wi]+vi≥L[i][j−2wi]+2vi=L[i][j−2wi]+vi+vi ,其中 L[i][j−wi] 為物體種類為 i ,背包大小j−wi的最大值,故 L[i][j−wi]≥L[i][j−2wi]+vi ,

綜上兩點可以得到,該遞推關系可以簡化為 L[i][j]=max{L[i−1][j],L[i][j−wi]+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