背包問題

前言: 總結(jié)模板

0X00 模板總結(jié)

首先背包問題對于「狀態(tài)」有一個(gè)通用的表示方法: dp[i][j]

代表使用前 i 個(gè)物體在不超過體積 j 的情況下的獲得的最大價(jià)值

理所應(yīng)當(dāng)可以根據(jù)第 i 個(gè)物品計(jì)算狀態(tài)

0 1 背包問題

01背包問題

根據(jù)第 i 個(gè)物品使不使用可以寫出「狀態(tài)轉(zhuǎn)移方程」:

dp[i][j] = max(dp[i-1][j], dp[i-1][j-v]+w)

意思就是使用該物品和不使用該物品取一個(gè)最大值

寫出 0 1 背包的通用解法:

n, cap = map(int, input().split())
mat = [[0] * 2 for _ in range(n+1)]
for x in range(1, n+1):
    mat[x][:] = map(int, input().split())
    
dp = [[0] * (cap + 1) for _ in range(n+1)]

for x in range(1, n+1):
    v, w = mat[x]
    for y in range(cap+1):
        dp[x][y] = dp[x-1][y]
        if y < v: continue
        dp[x][y] = max(dp[x][y], dp[x-1][y-v]+w)
        
print(dp[n][cap])

優(yōu)化成 1 維 dp

n, cap = map(int, input().split())
mat = [[0] * 2 for _ in range(n+1)]
for x in range(1, n+1):
    mat[x][:] = map(int, input().split())
    
dp = [0] * (cap + 1)

for x in range(1, n+1):
    v, w = mat[x]
    for y in range(cap, v-1, -1):
        dp[y] = max(dp[y], dp[y-v]+w)
        
print(dp[cap])

完全背包問題

完全背包問題

「完全背包」問題就是在 01 背包問題上加上了物品個(gè)數(shù)不限, 劃分從選不選變成了選幾個(gè)

n, cap = map(int, input().split())
mat = [[0] * 2 for i in range(n+1)]

for x in range(1, n+1):
    mat[x][:] = map(int, input().split())
    
dp = [[0] * (cap+1) for _ in range(n+1)]

for x in range(1, n+1):
    for y in range(cap+1):
        k, v, w = 0, mat[x][0], mat[x][1]
        while k * v <= y:
            dp[x][y] = max(dp[x][y], dp[x-1][y - k*v] + k*w)
            k += 1
            
print(dp[n][cap])

優(yōu)化成 O(n^2):

已知:

dp[x][y] = max(dp[x-1][y], dp[x-1][y-v] + w, dp[x-1][y-2*v]+2*w+...+dp[x-1][y-k*v]+k*w)  y > k*v

dp[x][y-v] = max(dp[x-1][y-v], dp[x-1][y-2*v] + w, ..., dp[x-1][y-k*v]+k*w) y > k*v

所以

dp[x][y] = max(dp[x-1][y], dp[x][y-v]+w)
n, cap = map(int, input().split())
mat = [[0] * 2 for i in range(n+1)]

for x in range(1, n+1):
    mat[x][:] = map(int, input().split())
    
dp = [[0] * (cap+1) for _ in range(n+1)]

for x in range(1, n+1):
    v, w = mat[x][0], mat[x][1]
    for y in range(cap+1):
        dp[x][y] = dp[x-1][y]
        if y < v: continue
        dp[x][y] = max(dp[x][y], dp[x][y - v] + w)
            
print(dp[n][cap])

優(yōu)化成一維 dp:

直接去掉一維, 由于是從當(dāng)前層來的, 所以順序是從小到大

n, cap = map(int, input().split())
mat = [[0] * 2 for i in range(n+1)]

for x in range(1, n+1):
    mat[x][:] = map(int, input().split())
    
dp = [0] * (cap+1)

for x in range(1, n+1):
    v, w = mat[x][0], mat[x][1]
    for y in range(v, cap+1):
        dp[y] = max(dp[y], dp[y - v] + w)
            
print(dp[cap])

多重背包問題

多重背包問題

「多重背包」問題就是在 01 背包問題上加上了物品個(gè)數(shù)有限, 劃分從選不選變成了選幾個(gè)

n, cap = map(int, input().split())
mat = [[0] * 3 for _ in range(n+1)]
for x in range(1, n+1):
    mat[x][:] = map(int, input().split())

dp = [[0] * (cap+1) for _ in range(n+1)]

for x in range(1, n+1):
    for y in range(cap+1):
        v, w, num = mat[x]
        for k in range(num+1):
            if k * v > y: break
            dp[x][y] = max(dp[x][y], dp[x-1][y - k*v]+k*w)

print(dp[n][cap])

優(yōu)化從 O(n*cap*s)O(n*cap*logs ) 其中:

  • s 是最多物品個(gè)數(shù)

基本原理如下:

0 ~ 200

可以被

1, 2, 4, 8, 16, 32, 64, 73

表示

所以任意一個(gè)物品的個(gè)數(shù)可以拆分, 比如有個(gè)物品的個(gè)數(shù)是 200 就可以由 代表著 1, 2, 4, 8, 16, 32, 64, 73 的 8 個(gè)新物品表示

最后使用 01 背包解決

n, cap = map(int, input().split())
mat = [[0, 0] for _ in range(15000)]
cnt = 1
for _ in range(1, n+1):
    v, w, s = map(int, input().split())
    k = 1
    while k <= s:
        mat[cnt][0], mat[cnt][1] = k*v, k*w
        cnt += 1
        s -= k
        k *= 2
    if s > 0:
        mat[cnt][0], mat[cnt][1] = s*v, s*w     
        cnt += 1

# 01 背包解決這個(gè)問題
n = cnt - 1
dp = [0] * (cap+1)

for x in range(n+1):
    v, w = mat[x]
    for y in range(cap, v-1, -1):
        dp[y] = max(dp[y], dp[y-v] + w)

print(dp[cap])

分組背包問題

分組背包問題的狀態(tài)表示方程的含義就有點(diǎn)變化了

dp[i][j] 表示使用 前 i 組物品, 在不超過 j 體積的情況下的最大價(jià)值

n, cap = map(int, input().split())

mat = [[]for _ in range(n+1)]

for x in range(1, n+1):
    m = int(input())
    for y in range(m):
        temp = list(map(int, input().split()))
        mat[x].append(temp)

dp = [[0] * (cap+1) for _ in range(n+1)]

for x in range(1, n+1):
    for y in range(cap+1):
        # 不使用 x 組
        dp[x][y] = dp[x-1][y]
        # 使用 x 組
        for k in range(len(mat[x])):
            v, w = mat[x][k]
            # 使用 1 次
            if y < v: continue
            dp[x][y] = max(dp[x][y], dp[x-1][y - v] + w)

print(dp[n][cap])
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末税灌,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子隐轩,更是在濱河造成了極大的恐慌图云,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卤材,死亡現(xiàn)場離奇詭異遮斥,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)扇丛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進(jìn)店門术吗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人帆精,你說我怎么就攤上這事较屿。” “怎么了卓练?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵隘蝎,是天一觀的道長。 經(jīng)常有香客問我襟企,道長嘱么,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任顽悼,我火速辦了婚禮曼振,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蔚龙。我一直安慰自己冰评,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布府蛇。 她就那樣靜靜地躺著集索,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上务荆,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天妆距,我揣著相機(jī)與錄音,去河邊找鬼函匕。 笑死娱据,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的盅惜。 我是一名探鬼主播中剩,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼抒寂!你這毒婦竟也來了结啼?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤屈芜,失蹤者是張志新(化名)和其女友劉穎郊愧,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體井佑,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡属铁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了躬翁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片焦蘑。...
    茶點(diǎn)故事閱讀 39,902評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖盒发,靈堂內(nèi)的尸體忽然破棺而出例嘱,到底是詐尸還是另有隱情,我是刑警寧澤迹辐,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布蝶防,位于F島的核電站甚侣,受9級特大地震影響明吩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜殷费,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一印荔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧详羡,春花似錦仍律、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春草则,著一層夾襖步出監(jiān)牢的瞬間钢拧,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工炕横, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留源内,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓份殿,卻偏偏與公主長得像膜钓,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子卿嘲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評論 2 354

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