1. 程式人生 > >wikioi天梯之1014 裝箱問題 入門動態規劃 【揹包問題】

wikioi天梯之1014 裝箱問題 入門動態規劃 【揹包問題】

題目

動態規劃一直不是很清楚 上網搜了一下 又見識了一個牛人 dd_engi寫的揹包九講 

把他的 01揹包問題 直接貼過來了

01揹包問題

題目

有N件物品和一個容量為V的揹包。第i件物品的費用是c[i],價值是w[i]。求解將哪些物品裝入揹包可使價值總和最大。

基本思路

這是最基礎的揹包問題,特點是:每種物品僅有一件,可以選擇放或不放。

用子問題定義狀態:即f[i][v]表示前i件物品恰放入一個容量為v的揹包可以獲得的最大價值。則其狀態轉移方程便是:

f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}

這個方程非常重要,基本上所有跟揹包相關的問題的方程都是由它衍生出來的。所以有必要將它詳細解釋一下:“將前i件物品放入容量為v的揹包中”這個子問題,若只考慮第i件物品的策略(放或不放),那麼就可以轉化為一個只牽扯前i-1件物品的問題。如果不放第i件物品,那麼問題就轉化為“前i-1件物品放入容量為v的揹包中”,價值為f[i-1][v];如果放第i件物品,那麼問題就轉化為“前i-1件物品放入剩下的容量為v-c[i]的揹包中”,此時能獲得的最大價值就是f[i-1][v-c[i]]再加上通過放入第i件物品獲得的價值w[i]。

優化空間複雜度

以上方法的時間和空間複雜度均為O(VN),其中時間複雜度應該已經不能再優化了,但空間複雜度卻可以優化到O。

先考慮上面講的基本思路如何實現,肯定是有一個主迴圈i=1..N,每次算出來二維陣列f[i][0..V]的所有值。那麼,如果只用一個數組f[0..V],能不能保證第i次迴圈結束後f[v]中表示的就是我們定義的狀態f[i][v]呢?f[i][v]是由f[i-1][v]和f[i-1][v-c[i]]兩個子問題遞推而來,能否保證在推f[i][v]時(也即在第i次主迴圈中推f[v]時)能夠得到f[i-1][v]和f[i-1][v-c[i]]的值呢?事實上,這要求在每次主迴圈中我們以v=V..0的順序推f[v],這樣才能保證推f[v]時f[v-c[i]]儲存的是狀態f[i-1][v-c[i]]的值。虛擬碼如下:

for i=1..N
    for v=V..0
        f[v]=max{f[v],f[v-c[i]]+w[i]};

其中的f[v]=max{f[v],f[v-c[i]]}一句恰就相當於我們的轉移方程f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]},因為現在的f[v-c[i]]就相當於原來的f[i-1][v-c[i]]。如果將v的迴圈順序從上面的逆序改成順序的話,那麼則成了f[i][v]由f[i][v-c[i]]推知,與本題意不符,但它卻是另一個重要的揹包問題P02最簡捷的解決方案,故學習只用一維陣列解01揹包問題是十分必要的。

事實上,使用一維陣列解01揹包的程式在後面會被多次用到,所以這裡抽象出一個處理一件01揹包中的物品過程,以後的程式碼中直接呼叫不加說明。

過程ZeroOnePack,表示處理一件01揹包中的物品,兩個引數cost、weight分別表明這件物品的費用和價值。

procedure ZeroOnePack(cost,weight)
    for v=V..cost
        f[v]=max{f[v],f[v-cost]+weight}

注意這個過程裡的處理與前面給出的虛擬碼有所不同。前面的示例程式寫成v=V..0是為了在程式中體現每個狀態都按照方程求解了,避免不必要的思維複雜度。而這裡既然已經抽象成看作黑箱的過程了,就可以加入優化。費用為cost的物品不會影響狀態f[0..cost-1],這是顯然的。

有了這個過程以後,01揹包問題的虛擬碼就可以這樣寫:

for i=1..N
    ZeroOnePack(c[i],w[i]);

初始化的細節問題

我們看到的求最優解的揹包問題題目中,事實上有兩種不太相同的問法。有的題目要求“恰好裝滿揹包”時的最優解,有的題目則並沒有要求必須把揹包裝滿。一種區別這兩種問法的實現方法是在初始化的時候有所不同。

如果是第一種問法,要求恰好裝滿揹包,那麼在初始化時除了f[0]為0其它f[1..V]均設為-∞,這樣就可以保證最終得到的f[N]是一種恰好裝滿揹包的最優解。

如果並沒有要求必須把揹包裝滿,而是隻希望價格儘量大,初始化時應該將f[0..V]全部設為0。

為什麼呢?可以這樣理解:初始化的f陣列事實上就是在沒有任何物品可以放入揹包時的合法狀態。如果要求揹包恰好裝滿,那麼此時只有容量為0的揹包可能被價值為0的nothing“恰好裝滿”,其它容量的揹包均沒有合法的解,屬於未定義的狀態,它們的值就都應該是-∞了。如果揹包並非必須被裝滿,那麼任何容量的揹包都有一個合法解“什麼都不裝”,這個解的價值為0,所以初始時狀態的值也就全部為0了。

這個小技巧完全可以推廣到其它型別的揹包問題,後面也就不再對進行狀態轉移之前的初始化進行講解。

一個常數優化

前面的虛擬碼中有 for v=V..1,可以將這個迴圈的下限進行改進。

由於只需要最後f[v]的值,倒推前一個物品,其實只要知道f[v-w[n]]即可。以此類推,對以第j個揹包,其實只需要知道到f[v-sum{w[j..n]}]即可,即程式碼中的

for i=1..N
    for v=V..0

可以改成

for i=1..n
    bound=max{V-sum{w[i..n]},c[i]}
    for v=V..bound

這對於V比較大時是有用的。

小結

01揹包問題是最基本的揹包問題,它包含了揹包問題中設計狀態、方程的最基本思想,另外,別的型別的揹包問題往往也可以轉換成01揹包問題求解。故一定要仔細體會上面基本思路的得出方法,狀態轉移方程的意義,以及最後怎樣優化的空間複雜度。

按照上面的講解可以把這題過了。。可憐

另外還有一個部落格仔細分析了一下這個揹包九講 可以參考一下

這個部落格分析的也可以

#include <iostream>
#include <cstring>

using namespace std;


int max(const int & a,const int & b){return a > b ? a : b;}

void ZeroOnePack(const int & cost,const int & value,int * f,const int & total)
{
    for(int i = total;i >= cost;i--)
    {
        f[i] = max(f[i],f[i - cost] + value);
    }
}


int main()
{
    int v,n;
    int f[20001];
    int a;
    cin>>v>>n;
    memset(f,0,sizeof(f));
//  cout<<sizeof(f)/sizeof(int)<<endl;//測試一下memset的清零情況。。無關緊要
//  for(unsigned i = 0;i < 20001;i++)
//  cout<<f[i]<<" ";

    for(int i = 0;i < n;i++)
    {
        cin>>a;
        if(a <= v)//是否能一次放入
        {
            ZeroOnePack(a,a,f,v);
        }
    }
    cout<<v -f[v]<<endl;


    return 0;
}

關於大牛dd_engi的資料 搜尋了一下 真是不簡單!

相關推薦

wikioi天梯1014 裝箱問題 入門動態規劃 揹包問題

題目 動態規劃一直不是很清楚 上網搜了一下 又見識了一個牛人 dd_engi寫的揹包九講  把他的 01揹包問題 直接貼過來了 01揹包問題 題目 有N件物品和一個容量為V的揹包。第i件物品的費用是c[i],價值是w[i]。求解將哪些物品裝入揹包可使價值總和最大。

入門動態規劃 洛谷P1108 低價購買

i++ 一支 股票 algorithm namespace 整數 變形 價格 div P1108 低價購買 題目描述 “低價購買”這條建議是在奶牛股票市場取得成功的一半規則。要想被認為是偉大的投資者,你必須遵循以下的問題建議:“低價購買;再低價購買”。每次你購買一支股票,你

演算法入門——動態規劃

用淺顯的話說動態規劃就是必須一環扣一環得去處理事務,如果有其中一環丟失了,那麼後續就會處理失敗了:比如皇帝要給所有的百姓免稅,並且要根據各個地方的政策免去不同數額的稅率。但是當時並不是網際網路時代,皇帝的聲音並不能響徹全國,也無法計算出各地的稅率。但又必須要讓全國人民知道。這時候就需要用到這個演

藍橋杯-裝箱問題-動態規劃-java

問題描述   有一個箱子容量為V(正整數,0<=V<=20000),同時有n個物品(0<n<=30),每個物品有一個體積(正整數)。   要求n個物品中,任取若干個裝入箱內,使箱子的剩餘空間為最小。 輸入格式   第一行為一個整數,表示箱子容量;   第二行為一個整數

裝箱問題---動態規劃

問題描述  有一個箱子容量為V(正整數,0<=V<=20000),同時有n個物品(0<n<=30),每個物品有一個體積(正整數)。  要求n個物品中,任取若干個裝入箱內,使箱子的剩餘空間為最小。輸入格式  第一行為一個整數,表示箱子容量;  第二行為一個整數,表示有n個物品;

MIT演算法導論公開課第15課 動態規劃、最長公共子序列

動態規劃(Dynamic programming) 動態規劃是一種設計技巧,而不是一種特定的演算法,就像分治法一樣。 最長公共子序列(Longest common subsequence)問題 有兩個序列,序列x[1~m],序列y[1~n],找到它們的最長

wikioi天梯3118 高精度練習除法

題目 做了天梯的加減乘然後又把除法找出來做了下 利用減法做除法 所以先要保證減法寫的是對的 這次直接寫減法 比上次速度提高了 而且竟然一次寫對了。 之前做過加減乘除 除法就沒有參考資料了 自己獨立寫的 最終也寫出來了。  #include <iostream>

演算法題動態規劃-01揹包問題

文字介紹解決揹包問題 假設山洞裡共有a,b,c,d ,e這5件寶物(不是5種寶物),它們的重量分別是2,2,6,5,4,它們的價值分別是6,3,5,4,6,現在給你個承重為10的揹包, 怎麼裝揹包,可以才能帶走最多的財富。 此時只要理解了狀態轉換方程f[i,j] = Max{ f[i-1,j-Wi]+Pi(

洛谷動態規劃/背包P1833 櫻花

ios clas 但是 輸入 ostream 代碼 iostream pan time 【題目描述:】 愛與愁大神後院裏種了n棵櫻花樹,每棵都有美學值Ci。愛與愁大神在每天上學前都會來賞花。愛與愁大神可是生物學霸,他懂得如何欣賞櫻花:一種櫻花樹看一遍過,一種櫻花樹最多看A

洛谷動態規劃(二維)P1508 Likecloud-吃、吃、吃

方程 con == dash fine 自己 pan ans amp 【題目描述:】 正處在某一特定時期之中的李大水牛由於消化系統比較發達,最近一直處在饑餓的狀態中。某日上課,正當他餓得頭昏眼花之時,眼前突然閃現出了一個n*m(n and m<=200)的矩型的巨型

洛谷動態規劃(多維)P1006 傳紙條

朋友 namespace PE n) 原來 這樣的 max 沒有 空間 【題目描述:】 小淵和小軒是好朋友也是同班同學,他們在一起總有談不完的話題。一次素質拓展活動中,班上同學安排做成一個m行n列的矩陣,而小淵和小軒被安排在矩陣對角線的兩端,因此,他們就無法直接交談了。幸

henuacm2016級暑期訓練動態規劃專題 DWriting Code

set 方案 pen col txt clu 二維 stream bit 【鏈接】 我是鏈接,點我呀:) 【題意】 在這裏輸入題意 【題解】 二維費用背包。 f[i][j][k] 前i個人,寫了j行,bug不超過k的方案數。 可以把每個人看成是一個物品。 它

henuacm2016級暑期訓練動態規劃專題 JRed-Green Towers

cin 使用 space open ble .net long clu 不用 【鏈接】 我是鏈接,點我呀:) 【題意】 在這裏輸入題意 【題解】 顯然最多1000行的樣子。 從上到小做dp 設f[i][j]為前i行,使用了j個紅色方塊的方案數。 f[1][r

Exploring Pyramids動態規劃——區間DP

Exploring Pyramids  UVALive - 3516  題目傳送門 題目大意:給你一個字串,其代表的是機器人來回走過的路徑,機器人總是先走左邊再走右邊,問有多少種情況。 解決方法:設輸入序列為S,d(i,j)為子序列Si,Si+1,…,Sj對應

動態規劃解決揹包問題

1 揹包問題:給定n個重量為w1,w2...wn,價值為v1,v2...vn的物品和一個承重量為W的揹包,求這些物品中最有價值的一個子集,並且要能夠裝入揹包當中。 2 動態規劃:動態規劃與分治法都要求原問題的最優子結構,都是將問題分而治之,,不同的是動態規劃適用於交疊子問題的情況,分治法則適用於子問題相互獨

動態規劃 -- 01揹包案例(python)

01揹包 m個物品價值為 p[m],體積為 w[m],現有一個容量為 v 的揹包,怎麼裝物品價值最大? 我們嘗試用動態規劃來解決這個問題 一般來說,只要問題可以劃分成規模更小的子問題,並且原問題的最優解中包含了子問題的最優解(即滿足最優子化原理),則可以

C++ 動態規劃 01揹包+ 最大字陣列和 +最短路徑 +斐波那契數列

int max(int a,int b) { return a>b?a:b; } /* 0 1 揹包 */ int MaxValue() { int Weight[5]={2,2,6,5,4};//物品的重量陣列 int Value

P2760 科技莊園-動態規劃揹包

Life種了一塊田,裡面種了有一些桃樹。 Life對PFT說:“我給你一定的時間去摘桃,你必須在規定的時間之內回到我面前,否則你摘的桃都要歸我吃!” PFT思考了一會,最終答應了! 由於PFT的數學不好!它並不知道怎樣才能在規定的時間獲得最大的價值, 由於PFT不是

動態規劃——01揹包問題

package pack01; public class PackTest_01 {     public static void main(String[] args) {         // TODO Auto-generated method stub      

987C___Three displays——動態規劃揹包+思維)

題目連結:點我 題目大意:     nnn個物品,每個物品體積為sis_isi​,花費為cic_ici​,每次選333個物品,使這三個物品i&lt;j&lt;ki&lt;j&lt;ki<j<k,並且si&lt;