演算法學習 - 01背包問題(動態規劃C++)

來源:互聯網
上載者:User

標籤:動態規劃   01背包   c++   

  • 動態規劃
  • 01背包
    • 問題描述
    • 求解思路
  • 代碼實現
  • 放入哪些物品
  • 代碼

動態規劃

我在上一篇部落格裡已經講了一點動態規划了,傳送門:演算法學習 - 動態規劃(DP問題)(C++)

這裡說一下,遇到動態規劃應該如何去想,才能找到解決辦法。

最主要的其實是要找狀態轉移的方程,例如上一篇部落格裡面,找的就是當前兩條生產線的第i個station的最短時間和上一時刻的時間關係。

minTime(station[1][i]) = minTime(station[1][i-1] + time[i], station[2][i-1] + time[i] + trans[i-1])

今天要講的問題是01背包問題。

01背包問題描述

01背包是在M件物品取出若干件放在空間為W的背包裡,每件物品的體積為W1,W2……Wn,與之相對應的價值為P1,P2……Pn。

求解這個背包能裝的最大價值。(物體不能分割)。

求解思路

這個其實比上一道題難在哪裡了呢?
難在上一題裝配線始終是兩個,不會多,不會少。我們變化的量只有裝配站的多少。

這個題目變化的量一個是物品的數量,還有一個是背包的空間。就是說裝配線不會隨著station的多少而變化,但是背包的空間會隨著物品的裝入而變化。

所以我們需要做的是判斷當背包剩餘的空間為j的時候,能否裝入第i個物品,並且總價值增加。
即:

d[j] = max ( d[j], d[j-v[i]] + w[i]); //v[i]表示第i個物品的體積,w[i]表示第i個物品的價值。

其實我最開始看到上面這個狀態轉移的方程的時候,覺得這不是肯定的在空間j的時候放入物品,價值大於不放入物品的。畢竟只要放入就證明增加了價值啊。

其實不是這個樣子的,因為只有在最開始都沒有裝入的時候,所有的都是0. 裝入就一定價值增加。但是假如我們的物品如下:

物品1: 空間 2 價值 5
物品2: 空間 4 價值 3

當我們放入第一件物品的時候,假設背包空間是5,那麼d[2…5]都是為 5. 因為d[0…1]空間不夠所以為0。 當我們放入物品2的時候,d[5] = 5 > d[5 - 4] + 3 因為d[1] = 0;

發現了沒有! 並不是放入就一定總價值增加!

所以我們遍曆所有物品,從第一個物品開始,找當空間為j的時候,裝入物品是否會增加價值!

代碼實現

代碼其實行數很少,不好看那些寫了兩個螢幕的,可能並沒有更多的功能。

////  main.cpp//  DP_01backpack////  Created by Alps on 15/4/28.//  Copyright (c) 2015年 chen. All rights reserved.//// 代碼中直接定義了石頭的數量和背包的空間,其實可以不用提前定義。// 這裡為了方便,而且在C++ 11中可以動態規定數組大小。#include <iostream>using namespace std;#ifndef STONE_NUM#define STONE_NUM 5 //定義石頭數量#endif#ifndef BACKPACK_SPACE#define BACKPACK_SPACE 10#endifint main(int argc, const char * argv[]) {    int stoneSpace, stoneValue;//儲存當前輸入的物品空間和價值    int value[BACKPACK_SPACE+1] = {0}; //初始化value數組    for (int i = 0; i < STONE_NUM; i++) {        scanf("%d %d", &stoneSpace, &stoneValue);//接收物品輸入        for (int j = BACKPACK_SPACE; j > 0; j--) {//當背包空間在不同的時候,** j 一定是從大到小**            if (j >= stoneSpace && value[j] < (value[j-stoneSpace] + stoneValue)) {//假如背包空間足夠,並且放入總價值增加                value[j] = value[j-stoneSpace] + stoneValue;//放入,並更新總價值            }        }    }    printf("%d\n", value[BACKPACK_SPACE]);    return 0;}

這裡解釋下,為何第二層for迴圈裡,j 是從最大變化到最小的。因為我們比較的是當前物品放入,價值是否有變化,就是 value[j] 和 value[j - v] 比較。 假如更新了,那麼 value[j + 1] 需要和 value[j + 1 - v] 比較的值,可能 value[j + 1 - v]已經被更新過了。就不行了!因為那個時候value[j + 1 -v]的總價值已經算上當前的物品了。再算就重複了。

放入哪些物品

上面的代碼沒有知道到底放入了哪些物品,其實是因為為了節省空間的,沒有儲存每個 stoneSpace和 stoneValue.
所以我們把它們變成數組就好了。

然後我們首先要做的是:找到一共背包用了多少空間!

這個很重要,因為背包的最大價值不一定是裝滿了,所以我們要找到用了多少空間,才能知道到底放入了哪些物品。

怎麼找呢?

假如我們的value數組如下:

0
0
4
6
9
10
13
15
15
19
19

很容易知道,這個背包剩餘了一個空間? 為什麼呢,因為最大的19有兩個,j 多了1但是價值沒有增加,說明這 1的空間沒有放入物品,也就是空餘了。

這樣找到第一個最大得數的下標,就是使用的空間了。

代碼

比較簡單,只不過多了一步尋找那些物品放入了,就是當背包空間減去物品空間,總價值也剛好增加了物品的價值數量,那麼說明這個物品被放入了。

代碼如下:

////  main.cpp//  DP_01backpack////  Created by Alps on 15/4/28.//  Copyright (c) 2015年 chen. All rights reserved.//// 代碼中直接定義了石頭的數量和背包的空間,其實可以不用提前定義。// 這裡為了方便,而且在C++ 11中可以動態規定數組大小。#include <iostream>using namespace std;#ifndef STONE_NUM#define STONE_NUM 5 //定義石頭數量#endif#ifndef BACKPACK_SPACE#define BACKPACK_SPACE 10#endifint main(int argc, const char * argv[]) {    int value[BACKPACK_SPACE+1] = {0};    int stoneSpace[STONE_NUM],stoneValue[STONE_NUM];    for (int i = 0; i < STONE_NUM; i++) {        scanf("%d %d", &stoneSpace[i], &stoneValue[i]);        for (int j = BACKPACK_SPACE; j > 0; j--) {            if (j >= stoneSpace[i] && value[j] < value[j-stoneSpace[i]] + stoneValue[i]) {                value[j] = value[j-stoneSpace[i]] + stoneValue[i];            }        }    }//以上代碼幾乎無變化,只是儲存是數組儲存了。    for (int i = 0; i <= BACKPACK_SPACE; i++) {        printf("%d\n", value[i]);    }//列印value數組,這裡面存放的是,所有物品在背包只有空間i的時候,達到的最大總價值。    int backPackSpace = BACKPACK_SPACE;// 得到背包空間    while (value[backPackSpace] == value[backPackSpace-1]) {        backPackSpace--;//假如背包價值和背包空間-1的時候價值相同,空間 -1    }//找到一共使用了多少空間    for (int i = STONE_NUM-1; i >= 0; i--) {        if (value[backPackSpace] == value[backPackSpace-stoneSpace[i]] + stoneValue[i]) { //假如減去當前物品的空間,總價值剛好和物品的價值相等,說明此物品被放入了。            printf("%d ", i+1);//列印這個物品。i+1是因為物品的下標是從0開始的。            backPackSpace = backPackSpace - stoneSpace[i]; //背包放入了這個物品,自然空間減少了。        }    }    return 0;}

整個代碼比較簡單。 有疑問的留言,可以相互交流。

我看了一篇其他部落格,用二維矩陣入門,也覺得很棒。推薦:http://www.hawstein.com/posts/dp-knapsack.html

演算法學習 - 01背包問題(動態規劃C++)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.