揹包問題,是動態規劃問題中的典型的一類。顧名思義,是跟揹包有關的問題(居然和名字有關,爺青結)。ios
大概就是講揹包空間有限,怎樣合理地裝物品可讓總價值最高的問題。c++
其實也不是很是難。數組
揹包問題的主要難點也就在於種類繁多,須要記憶各類遞推公式、循環結構。優化
不過畢竟是記憶而已,老是比其餘須要現場推倒遞推公式的要好很多。atom
本文主要講如下幾類:spa
一個旅行者有一個最多能裝 MM 公斤的揹包,如今有 nn 件物品,它們的重量分別是W1,W2,...,WnW1,W2,...,Wn,它們的價值分別爲C1,C2,...,CnC1,C2,...,Cn,求旅行者能得到最大總價值。code
第一行:兩個整數,MM(揹包容量,M≤200M≤200)和NN(物品數量,N≤30N≤30);xml
第2..N+12..N+1行:每行二個整數Wi,CiWi,Ci,表示每一個物品的重量和價值。blog
僅一行,一個數,表示最大總價值。it
10 4 2 1 3 3 4 5 7 9
12
既然要用用動態規劃法解決0-1揹包問題,咱們就先定義動態規劃的三個要點,即狀態、狀態轉移方程和邊界條件。
首先咱們用子問題定義狀態,咱們用F(i,j)表示「把前i件物品放入容量爲j的揹包中的最大總重量」。
而後咱們要考慮怎樣的狀態轉移方程能夠把這個問題轉化爲更小的子問題。咱們依然以「每個物品都有放或不放兩種選擇」的策略爲基礎,考慮第i件物品,若是咱們選擇不放第i件物品,那麼問題就直接轉化爲「把前i-1件物品放入容量爲j的揹包中的最大總重量」,若是咱們選擇放第i件物品,那麼問題就轉化爲「把前i-1件物品放入容量爲j-V[i]的揹包中的最大總重量加上第i件物品的重量」(這裏值得注意的一點是,若是j-V[i]<0,即放入第i件物品後超過了揹包容量的限制,那麼咱們就只能選擇不放第i件物品了)。因此狀態轉移方程爲F(i,j)=max{F(i-1,j),F(i-1,j-V[i])+W[i]}。
邊界條件則很容易獲得,i=0時F(i,j)爲0(沒有物品就沒有重量),j<0時F(i,j)爲負無窮(但在代碼中並不會這樣初始化,咱們在j-V[i]<0時不計算第二種狀況便可)。最終答案則是f(n,C)。
但其實這個空間複雜度是能夠優化的。
能夠直接用f[i]來表示當重量爲i時,能夠有的最大價值。
狀態轉移方程:
f[j] = max(f[j], f[j-c[i]]+w[i])
到了這裏,終於要講循環順序的問題了。
其實第二層循環爲何要反向的問題,我也研究了很久,終於在看一篇文章的時候恍然大悟。
根據題意,每種物品只有一件。
當咱們循環的時候,若是正序,那天然就會從前到後的更新數組。
那若是揹包容量大於某件物品的多倍呢?
而剛好這件物品性價比很高?
假設a是一個常數。這件物品的質量爲w。
那麼,好比說,咱們在循環到i=a的時候,把這件物品裝進了揹包。
那再循環到i=a+w的時候,可能又會把這件物品裝進揹包。
也就是說,這件物品被使用了兩次,甚至後面可能更多。
而根據題意,每件物品只有一個。
而對於每一次更新,只會用到i比當前小的數據,而不會用到i比當前大的。(由於要查看f[i-w[j]],w[j]不多是負的)
因此,先把大的更新了是沒有問題的。
(學了揹包問題這麼久,終於把這個問題解決了!!!開心!!!)
#include <iostream> #include <cstdio> #define M 1000 using namespace std; int f[M], c[M], w[M]; int ans, v, m; int main() { scanf("%d%d", &v, &m); for(int i = 1; i <= m; i++) { scanf("%d%d", &c[i], &w[i]); } for(int i = 1; i <= m; i++) { for(int j = v; j >= c[i]; j--) { f[j] = max(f[j], f[j-c[i]]+w[i]); } } printf("%d\n", f[v]); return 0; }
設有nn種物品,每種物品有一個重量及一個價值。但每種物品的數量是無限的,同時有一個揹包,最大載重量爲MM,今從nn種物品中選取若干件(同一種物品能夠屢次選取),使其重量的和小於等於MM,而價值的和爲最大。
第一行:兩個整數,MM(揹包容量,M≤200M≤200)和NN(物品數量,N≤30N≤30);
第2..N+12..N+1行:每行二個整數Wi,CiWi,Ci,表示每一個物品的重量和價值。
僅一行,一個數,表示最大總價值。
10 4 2 1 3 3 4 5 7 9
max=12
那麼,既然剛纔講了01揹包由於每種物品只有一個因此只能逆序循環,徹底揹包問題天然就是把循環順序改成順序就能夠了!
——就這?就這?就這?
——對,就這。
——啊這,淚目
[doge]
#include <iostream> #include <cstdio> #define M 1000 using namespace std; int f[M], c[M], w[M]; int ans, v, m; int main() { scanf("%d%d", &v, &m); for(int i = 1; i <= m; i++) { scanf("%d%d", &c[i], &w[i]); } for(int i = 1; i <= m; i++) { for(int j = c[i]; j <= v; j++) { f[j] = max(f[j], f[j-c[i]]+w[i]); } } printf("max=%d\n", f[v]); return 0; }
爲了慶賀班級在校運動會上取得全校第一名成績,班主任決定開一場慶功會,爲此撥款購買獎品犒勞運動員。指望撥款金額能購買最大價值的獎品,能夠補充他們的精力和體力。
第一行二個數n(n≤500),m(m≤6000),其中n表明但願購買的獎品的種數,m表示撥款金額。
接下來n行,每行3個數,v、w、s,分別表示第I種獎品的價格、價值(價格與價值是不一樣的概念)和能購買的最大數量(買0件到s件都可),其中v≤100,w≤1000,s≤10。
一行:一個數,表示這次購買能得到的最大的價值(注意!不是價格)。
5 1000 80 20 4 40 50 9 30 50 7 40 30 6 20 20 1
1040
其實多重揹包問題,雖然一個物品有了好幾個,可是仍然能夠按照01揹包問題的思路,每一個物品能取幾件就存儲幾回就行了(當作不一樣的物品)
#include <bits/stdc++.h> #define M 1000 using namespace std; int f[M], c[M], w[M],num[M]; int ans, v, m; int main() { scanf("%d%d", &v, &m); for(int i = 1; i <= m; i++) { scanf("%d%d%d", &c[i], &w[i],&num[i]); } int jjc=m; for (int i = 1; i <= m; ++i) { for (int j = 1; j <= num[i]; ++j) { c[jjc]=c[i]; w[jjc]=w[i]; jjc++; } } for(int i = 1; i <= m; i++) { for(int j = c[i]; j <= v; j++) { f[j] = max(f[j], f[j-c[i]]+w[i]); } } printf("max=%d\n", f[v]); return 0; }
寵物小精靈是一部講述小智和他的搭檔皮卡丘一塊兒冒險的故事。
一天,小智和皮卡丘來到了小精靈狩獵場,裏面有不少珍貴的野生寵物小精靈。小智也想收服其中的一些小精靈。然而,野生的小精靈並不那麼容易被收服。對於每個野生小精靈而言,小智可能須要使用不少個精靈球才能收服它,而在收服過程當中,野生小精靈也會對皮卡丘形成必定的傷害(從而減小皮卡丘的體力)。當皮卡丘的體力小於等於0時,小智就必須結束狩獵(由於他須要給皮卡丘療傷),而使得皮卡丘體力小於等於0的野生小精靈也不會被小智收服。當小智的精靈球用完時,狩獵也宣告結束。
咱們假設小智遇到野生小精靈時有兩個選擇:收服它,或者離開它。若是小智選擇了收服,那麼必定會扔出可以收服該小精靈的精靈球,而皮卡丘也必定會受到相應的傷害;若是選擇離開它,那麼小智不會損失精靈球,皮卡丘也不會損失體力。
小智的目標有兩個:主要目標是收服儘量多的野生小精靈;若是能夠收服的小精靈數量同樣,小智但願皮卡丘受到的傷害越小(剩餘體力越大),由於他們還要繼續冒險。
如今已知小智的精靈球數量和皮卡丘的初始體力,已知每個小精靈須要的用於收服的精靈球數目和它在被收服過程當中會對皮卡丘形成的傷害數目。請問,小智該如何選擇收服哪些小精靈以達到他的目標呢?
輸入數據的第一行包含三個整數:N(0<N<1000),M(0<M<500),K(0<K<100),分別表明小智的精靈球數量、皮卡丘初始的體力值、野生小精靈的數量。
以後的K行,每一行表明一個野生小精靈,包括兩個整數:收服該小精靈須要的精靈球的數量,以及收服過程當中對皮卡丘形成的傷害。
輸出爲一行,包含兩個整數:C,R,分別表示最多收服C個小精靈,以及收服C個小精靈時皮卡丘的剩餘體力值最多爲R。
10 100 5 7 10 2 40 2 50 1 20 4 20
3 30
10 100 5 8 110 12 10 20 10 5 200 1 110
0 100
對於樣例輸入2:小智一個小精靈都無法收服,皮卡丘也不會收到任何傷害,因此輸出0 100。
一個旅行者有一個最多能裝V公斤的揹包,如今有n件物品,它們的重量分別是W1,W2,...,WnW1,W2,...,Wn,它們的價值分別爲C1,C2,...,CnC1,C2,...,Cn。這些物品被劃分爲若干組,每組中的物品互相沖突,最多選一件。求解將哪些物品裝入揹包可以使這些物品的費用總和不超過揹包容量,且價值總和最大。
第一行:三個整數,V(揹包容量,V≤200),N(物品數量,N≤30)和T(最大組號,T≤10);
第2..N+1行:每行三個整數Wi,Ci,PWi,Ci,P,表示每一個物品的重量,價值,所屬組號。
僅一行,一個數,表示最大總價值。
10 6 3 2 1 1 3 3 1 4 8 2 6 9 2 2 8 3 3 9 3
20