背包問題與動態(tài)規(guī)劃初探

動態(tài)規(guī)劃鞠评,是算法初學(xué)者怎么也繞不開的大山……它分外讓人頭疼,云山霧繞聋涨,不得要領(lǐng)负乡。我感覺,它其實是一種新的思維方式茂腥,讓人學(xué)會打破常規(guī)思路去看待問題(只是我個人作為小白的不成熟的小領(lǐng)悟)最岗。

動態(tài)規(guī)劃中朝捆,有個經(jīng)典的背包問題,也被稱為0-1背包問題驯用。這個問題是假設(shè)我有一個包儒老,什么都想往里裝,但是包就這么大淘这,我想盡量裝多點值錢的東西進去,怎么辦钠怯?

這個問題看似炒雞簡單且貼近生活曙聂,但是實質(zhì)上暗藏玄機,所以就有很多種很多種解法断国。

一般來說榆苞,根據(jù)生活常識坐漏,應(yīng)該選擇先裝最貴的,但是這里有個致命的問題:如果最貴那個它體積也很大街夭,那最后綜合結(jié)果可能還不如裝單價不那么貴但可以多塞幾個的躏筏。

那然后我們就可以說,不然選取標準換成單位價值(直白點說就是性價比)埃碱,意思就是:每個貨物的價值除以它的體積乃正,這樣就可以避免只看那些雖然單價挺值錢但是大的要死的物品婶博,但是問題并沒有得到解決凡人,因為背包的容量是一定的叹阔,無論如何在放每個物品前都要考慮的是:放了這個還能不能放別的,萬一這個一個就把包裝滿了而放別的性價比低可以多塞幾個更劃算咋辦岸晦?

那怎么解決到底拿哪些物品可以達到最優(yōu)解呢?

正常人的思路都是從暴力求解法開始的启上,畢竟計算機就擅長這個,只要規(guī)則清晰明了倒慧,計算過程再復(fù)雜都不怕包券。其實每個物品都有兩種選擇:拿它,不拿付秕,這也就是0-1背包問題的由來询吴,拿了就是1,不拿就是0汰寓。那苹粟,大不了就把所有的組合都列一遍出來唄嵌削,只要最后選擇拿的物品體積之和不大于包的容量就算一個可能的組合方式,再在這些可能解里找一個最大的值肌访,看起來沒啥問題艇劫。

有問題嗎?有蟹演。最大的問題是顷蟀,物品少些還成,物品稍微多一點羞反,每加一個物品,循環(huán)就深一層是趴,時間復(fù)雜度以指數(shù)形式坐火箭一樣蹭蹭蹭往上竄膏秫。我們都知道,最壞最壞的算法窘哈,就是把時間復(fù)雜度搞成指數(shù)形式的亭敢,最好想都不要想,所以暴力求解法對背包問題來說只能是一個大大的NO让腹。

硬攻搞不定骇窍,就只能學(xué)習(xí)智取了。

上面說到腹纳,0-1背包問題翻譯一下是這樣:有一長串兒的物品清單嘲恍,每個都可以選擇取或者不取雄驹,唯一的限制就是物品總量不能超過背包限制医舆,并且物品是無法分割的。那么其實對每個物品取用狀態(tài)都會對背包的剩余容積造成影響蔬将,這是一點娃胆。還有就是里烦,假設(shè)我們是按照清單按順序去查看每一個物品的,那么輪到每個物品的時候胁黑,我們都不太需要關(guān)注前面取了什么,只需要看背包剩余容量還能不能裝漂洋,以及當前物品還要不要裝這兩件事力喷。

為什么要這么想呢?這就涉及到動態(tài)規(guī)劃一些基本的概念了贝咙。

首先要明確一點是拂募,動態(tài)規(guī)劃是個很方便的好登西,它可以把紛紛擾擾充滿無數(shù)種可能的問題(比如指數(shù)級問題)轉(zhuǎn)化為計算復(fù)雜度固定的問題蔼水,只不過需要犧牲一點兒空間來換扰恳浮(畢竟世界很難兩全其美)嘁信。它的思路呢,是化繁為簡穿剖,把我們要解決的看似有無窮選擇的復(fù)雜問題轉(zhuǎn)化為子問題的遞進解決糊余。在我個人看來贬芥,動態(tài)規(guī)劃和遞歸的思路有異曲同工之妙,只不過逼近真相的方向相反蘸劈。遞歸代碼上很簡潔明了威沫,但是不可避免地有大量重復(fù)計算,造成浪費孵构,而動態(tài)規(guī)劃可以利用起這些中間結(jié)果烟很,一步步接近真相。

只可惜恤筛,如此化繁為簡的良方叹俏,不是所有的問題都可以拿來如此這般抽絲剝繭抓住要害的粘驰。可以用動態(tài)規(guī)劃來解決的問題都必須滿足:最優(yōu)解的子問題也需要是最優(yōu)解蝌数。還有:前面做的選擇不會影響到后面的選擇度秘。第一個條件還比較好理解,因為本來動態(tài)規(guī)劃就是一步步推到最終結(jié)果的唆貌,假如前面的選擇好壞都無所謂锨咙,那最后結(jié)果還怎么保證最優(yōu)呢?第二個條件看起來稍有些迷惑——因為很顯然酪刀,上一個東西裝了可能會導(dǎo)致下一個裝不進去钮孵,這難道不算是對后面的選擇有影響嗎?答案是:不算巴席!因為每一個物品選擇裝或不裝時,它們之間都是相互獨立的荧库,我不關(guān)心前面是不是選了A物品电爹,我只關(guān)心前面所有物品占的體積,還有它們產(chǎn)生的最大價值料睛,但是不會因為有了A物品就不可以選擇B物品這樣的限制丐箩,所以這就是無后效性。

我們回到背包問題本身恤煞,就剛剛的條件而言屎勘,它滿足動態(tài)規(guī)劃的基本前提,所以可以用動態(tài)規(guī)劃來做居扒。我們需要找到它的子問題概漱,下面的話有些繁瑣,但是對于理解這個問題來說是有一些用處的:對于每一個物品而言喜喂,都有裝和不裝的選項瓤摧,假設(shè)此時問題中有5個物品,那么我們只看選到第5個物品時的情況(假設(shè)前面我們已經(jīng)選到了用這個包裝4個物品最值錢的取法)∮裼酰現(xiàn)在第五個物品擺在眼前照弥,首先想想是不是可裝:這第五個物品本身有沒有超過了背包的總?cè)萘浚咳绻^了只能直接放棄给赞;下一步如果確定可以裝,就要決定要不要裝障涯?判斷準則也很明確:裝的話需要保證這個取法更值錢(起碼跟前四個物品最佳方案一樣值錢,不然不如不裝)。

仔細想想都弹,第五個物品在背包容量足夠裝的情況下,判斷值錢的裝法框杜,就是在選它所帶來更新后的收益,和不選它的原來的收益里選一個最大值。不選它的收益专筷,就跟用這個背包裝四個物品的最優(yōu)解是一樣的,而選它的收益的計算方法是把背包里第五個物品將會占的空間去掉,剩下空間在只有四個物品時的最優(yōu)解(假設(shè)我們也知道縮小的背包裝四個物品最值錢的解法)加上第五個物品的價值莺葫。這兩種收益選其中大值堡纬,就是我們要的烤镐,用背包裝五個物品的最優(yōu)解了。

上面這一大串啰里八嗦的話就是我對背包問題子問題的理解了祟辟,如果問題就是5個物體的選擇,那么根據(jù)上面的思維過程叮喳,我們需要從算出1個物品裝進背包最優(yōu)解剩晴,推到2個趣兄,3個,4個才能一層層推出5個物體的最優(yōu)解鲁纠;同樣迄汛,因為每加入一個新的物品都可能會用到剩余空間在沒放這個物品情況下的最優(yōu)解,所以選取組合容量也必須從1開始每一個值都計算,直到到背包真實容量為止躬柬。

這就是為什么背包問題的動態(tài)規(guī)劃初始形態(tài)是填一張二維的表格,這個表格的縱軸是每個物品(應(yīng)該理解為物品的累加:1-表示可選擇的有物品1,2-表示可選擇的有物品1琼掠,2……以此類推),橫軸表示容量從1-遞增到背包的容量上限戈毒。這張表格里的每一個格子表示:在當前的容量j下冠桃,可選的物品有1碳蛋,2笤受,…i 這些身坐,此時的最優(yōu)解涯鲁。

實際的代碼中警绩,會多出一行表示當物體為0(意味著沒有物品可放)的值咧擂,和一列背包容量為0(意味著背包什么也放不了)的值贸桶,它們在0-1背包問題中默認初始值都為0旗笔。前者是為初始化撮弧,這樣放第一個物品的時候,如果背包裝不下裙椭,就等同于沒有物品可放;而后者是為了計算背包正好裝滿的時候,求剩余空間為0的最優(yōu)解,可以正確返回0。

下面獻上我很丑的代碼:

#!/usr/bin/env python
# -*- coding:utf-8 -*-

def backpack_0_1(items,capacity,weights,values):
    # 初始化表格
    dp = [[0 for _ in range(capacity+1)] for _ in range(items+1)]
    solution = []
    for i in range(1,items+1):
        for j in range(1,capacity+1):
            if j <weights[i-1]:
                dp[i][j] = dp[i-1][j]
            else:
                dp[i][j] = max(dp[i-1][j],dp[i-1][j-weights[i-1]]+values[i-1])
    # 返回物品選取方案
    i = items
    j = capacity
    while i > 0:
        print(i,j)
        if dp[i][j] > dp[i-1][j]:
            solution.append(i)
            j -= weights[i-1]
        i -= 1
    return solution,dp[-1][-1]

print(backpack_0_1(4,10,[2,3,5,5],[2,4,3,7]))
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末斜棚,一起剝皮案震驚了整個濱河市弟蚀,隨后出現(xiàn)的幾起案子粗梭,更是在濱河造成了極大的恐慌,老刑警劉巖兔簇,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異劳吠,居然都是意外死亡缆毁,警方通過查閱死者的電腦和手機番川,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來脊框,“玉大人颁督,你說我怎么就攤上這事〗奖ⅲ” “怎么了沉御?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長昭灵。 經(jīng)常有香客問我吠裆,道長伐谈,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任试疙,我火速辦了婚禮诵棵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘祝旷。我一直安慰自己履澳,他們只是感情好,可當我...
    茶點故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布怀跛。 她就那樣靜靜地躺著距贷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪敌完。 梳的紋絲不亂的頭發(fā)上储耐,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天,我揣著相機與錄音滨溉,去河邊找鬼什湘。 笑死,一個胖子當著我的面吹牛晦攒,可吹牛的內(nèi)容都是我干的闽撤。 我是一名探鬼主播,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼脯颜,長吁一口氣:“原來是場噩夢啊……” “哼哟旗!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起栋操,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤闸餐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后矾芙,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體舍沙,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年剔宪,在試婚紗的時候發(fā)現(xiàn)自己被綠了拂铡。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,646評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡葱绒,死狀恐怖感帅,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情地淀,我是刑警寧澤失球,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站帮毁,受9級特大地震影響实苞,放射性物質(zhì)發(fā)生泄漏璧微。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一硬梁、第九天 我趴在偏房一處隱蔽的房頂上張望前硫。 院中可真熱鬧,春花似錦荧止、人聲如沸屹电。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽危号。三九已至,卻和暖如春素邪,著一層夾襖步出監(jiān)牢的瞬間外莲,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工兔朦, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留偷线,地道東北人。 一個月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓沽甥,卻偏偏與公主長得像声邦,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子摆舟,可洞房花燭夜當晚...
    茶點故事閱讀 43,514評論 2 348

推薦閱讀更多精彩內(nèi)容