动态规划问题之背包模型(18题)
背包問題是動(dòng)態(tài)規(guī)劃問題的一大類型,下面我們對(duì)這個(gè)進(jìn)行總結(jié)。
以 Acwing y中總結(jié)的 幾個(gè)類型,我寫了幾個(gè)題解
應(yīng)用知識(shí)點(diǎn)
- 01背包、完全背包 空間壓縮的寫法
- 多維費(fèi)用的背包問題,以及狀態(tài)的不同表示對(duì)復(fù)雜度的 影響
- 完全背包問題的三種求解方法O(NMS),O(NMlogS),O(NM)O(NMS), O(NMlogS),O(NM)O(NMS),O(NMlogS),O(NM)
- dp維度關(guān)系等于,小于等于,大于等于對(duì)初始化的影響(潛水員)
- dp關(guān)鍵信息最小值,最大值,方案數(shù)對(duì)初始化的影響
一、采藥
Acwing 題目鏈接
非常裸的 01 背包
二、裝箱問題
ACwing 題目鏈接
對(duì)于該問題,我給出兩個(gè)解法(其實(shí)都類似 )
第一種
f[i][j] 表示利用前 i 個(gè)箱子,體積恰好為 j 的方案是否可行,是一個(gè) bool 數(shù)組
那么 f[i][j]=f[i?1][j]∣f[i?1][j?v[i]]f[i][j] = f[i-1][j] | f[i-1][j - v[i]]f[i][j]=f[i?1][j]∣f[i?1][j?v[i]]
初始化時(shí), f[0][0]f[0][0]f[0][0] 置為 true,其余置為 false
下面是進(jìn)行空間優(yōu)化的寫法
第二種寫法
f[i][j]f[i][j]f[i][j]表示的是對(duì)前 i 個(gè)箱子,體積小于等于 j 時(shí)候的最大體積
f[i][j]=max(f[i?1][j],f[i?1][j?v[i]]+v[i]f[i][j] =max(f[i-1][j], f[i-1][j-v[i]] + v[i]f[i][j]=max(f[i?1][j],f[i?1][j?v[i]]+v[i]
這樣的話,初始化全部為 0
而且最后輸出結(jié)果也比較簡(jiǎn)單
m?f[n][m]m - f[n][m]m?f[n][m]
下面給出空間優(yōu)化之后的代碼
三、寵物小精靈值收服
Acwing 題目鏈接
本題是一個(gè)二維費(fèi)用的 01 背包,理解起來不拿,不過題目屬實(shí)有點(diǎn)長(zhǎng),而且 皮卡丘 的血量為 0 也會(huì)抓取失敗,這是一個(gè)比較坑的點(diǎn)
下面,我給出兩個(gè)dp的解決方案
方案一
f[i][j][k]f[i][j][k]f[i][j][k] 表示對(duì)前 i 個(gè)怪獸進(jìn)行遍歷,消耗精靈球數(shù)量小于等于j,消耗血量小于等于 k,的最大抓捕量
f[i][j][k]=max(f[i?1][k][k],f[i?1][j?cost1i][k?cost2i])f[i][j][k] = max(f[i-1][k][k], f[i-1][j-cost1_i][k-cost2_i])f[i][j][k]=max(f[i?1][k][k],f[i?1][j?cost1i?][k?cost2i?])
對(duì)應(yīng)的代碼如下
但是我們將復(fù)雜度考慮進(jìn)去的話,O(KNM)O(KNM)O(KNM)有時(shí)候會(huì)過大,萬(wàn)一被卡怎么辦? 給出方案二
方案二
f[i][M][K]f[i][M][K]f[i][M][K] 遍歷前面i個(gè)精靈,統(tǒng)計(jì)的是在 體力恰好為 m, 捕捉恰好為 k 時(shí)候的最小消耗求的數(shù)量
復(fù)雜度O(KKM)O(KKM)O(KKM)快了那么一點(diǎn)點(diǎn)
f[i][[j][k]=min(f[i?1][j][k],f[i?1][m?cost2i][k?1]+cost1i)f[i][[j][k]=min(f[i-1][j][k], f[i-1][m-cost2_i][k-1]+cost1_i)f[i][[j][k]=min(f[i?1][j][k],f[i?1][m?cost2i?][k?1]+cost1i?)
代碼如下
DP很靈活,有時(shí)候換一個(gè)dp思路,就可以優(yōu)化一下復(fù)雜度
四、數(shù)字組合
Acwing 題目鏈接
挺簡(jiǎn)單一個(gè) dp問題,和 01 背包很像
下面給出空間優(yōu)化的代碼
五、買書
題目鏈接
挺簡(jiǎn)單的一個(gè) 完全背包問題
將書的個(gè)數(shù)看成 value,花費(fèi)就是費(fèi)用,然后跑一個(gè)完全背包
六、貨幣系統(tǒng)
Problem Link
題目大意就是 在 n 個(gè)數(shù) a1,a2,…,ana_1,a_2,\dots,a_na1?,a2?,…,an?中,盡可能的選取少的數(shù)字將原數(shù)組給表示出來
解題思路如下
- 首先,我們先將 原數(shù)組 a 從小到大進(jìn)行排序
- 最小的數(shù)字是不可以被表示的,因此最小的數(shù)字 a1a_1a1?需要被選出,放在bbb數(shù)組中
- 然后我們查看 b 數(shù)組可以組合出哪些數(shù)字
- 不斷對(duì) a 數(shù)組的元素進(jìn)行遍歷,倘若他無法被 b 表示,那么他需要加入到 b 中,這是因?yàn)?如果 a[i]a[i]a[i] 無法被 a[0..i]a[0..i]a[0..i]所表示,那么他就無法被表示,需要放入支撐集 b 中
這就是一個(gè)完全背包的模型
/*這次的dp需要考慮到最小依賴集這個(gè)東西:對(duì)于 (n, a) ==> (m, b):那么:1. a1、、an一定是可以被b表示出來的2. b 一定是屬于 a, 主要是因?yàn)樘热?b 不屬于 a, 那么 b = sum(a[i] * t[i]) = sum(b[i] * t[i])自己被自己表示3. b不能被自己表示因此這個(gè)問題轉(zhuǎn)換為了 求 不能被自己組成的a,然后作為b的一部分,也就是一個(gè)完全背包題目;代碼簡(jiǎn)單,但是思路有點(diǎn)意思 */ #include <bits/stdc++.h> using namespace std;const int M = 25010, N = 110; int a[N], n, m; bool f[M];void sol() {sort(a + 1, a + 1 + n);m = a[n];memset(f, 0, sizeof f);f[0] = true;int res = 0;for (int i = 1; i <= n; i ++ ) {if (f[a[i]]) {continue;}// 無法被表示,需要被加入res ++;for (int j = a[i]; j <= m; j ++ ) {f[j] |= f[j - a[i]];}}printf("%d\n", res);}int main() {int T; cin >> T;while (T -- ) {scanf("%d", &n);for (int i = 1; i <= n; i ++ ) {scanf("%d", &a[i]);}sol();} }七、多重背包問題III
多重背包問題鏈接
普通多重背包問題樸素寫法O(NMS)O(NMS)O(NMS)
f(i,j)f(i, j)f(i,j)表示前i個(gè)物品,容量小于等于jjj的最大價(jià)值
多重背包問題二進(jìn)制優(yōu)化方法O(NMlogS)O(NMlogS)O(NMlogS)
首先,對(duì)于容量,價(jià)值,和數(shù)量分別為?vi,wi,siv_i,w_i,s_ivi?,wi?,si?的物品iii,我們可以將它分解為
多個(gè) 01 背包的物品
倘若 si=10s_i = 10si?=10,我們可以將其分為
????(vi,wi),(2vi,2wi),(4vi,4wi),(3vi,3wi)(v_i,w_i),(2v_i,2w_i),(4v_i,4w_i),(3v_i,3w_i)(vi?,wi?),(2vi?,2wi?),(4vi?,4wi?),(3vi?,3wi?),這幾個(gè) 01背包,可以將
????(vi,wi),(2vi,2wi),(3vi,3wi),…(siwi,sivi)(v_i,w_i),(2v_i,2w_i),(3v_i,3w_i), \dots (s_iw_i,s_i v_i)(vi?,wi?),(2vi?,2wi?),(3vi?,3wi?),…(si?wi?,si?vi?)全部給枚舉一遍
這個(gè)是可以證明的
????(w,v),(2w,2v),(4w,4v),(2kw,2kv),(surplus?w,surplus?v),其中surplus≥2k,1+2+4+?+2k+surplus=s(w,v),(2w,2v),(4w,4v),(2^kw,2^kv),(surplus*w,surplus*v), 其中surplus\geq 2^k,1+2+4+\dots+2^k+surplus=s(w,v),(2w,2v),(4w,4v),(2kw,2kv),(surplus?w,surplus?v),其中surplus≥2k,1+2+4+?+2k+surplus=s
????首先1,2,4,?,2k1,2,4,\cdots,2^k1,2,4,?,2k可以將[0,2k+1][0,2^k+1][0,2k+1]完全包含,通過surplussurplussurplus區(qū)間再次移動(dòng),將[0,s][0,s][0,s]方案完全覆蓋掉。
對(duì)用代碼如下
完全背包的優(yōu)化,類似于前綴最大值,
然后我們多重背包的優(yōu)化,利用單調(diào)隊(duì)列的優(yōu)化
下面我來介紹優(yōu)化的方法
首先,我們還是先對(duì) i 個(gè)物品進(jìn)行遍歷
for(int i=1;i <= n; i ++ )
此時(shí),我們第 i 個(gè)物品的體積為 v,價(jià)值為w,可用數(shù)量為 j
考慮一下我們的狀態(tài)轉(zhuǎn)移方程f[i][j]=max(f[i?1][j],f[i?1][j?v]+w,f[i?1][j?2v]+2w…,f[i?1][j?sv]+sw)f[i][j] = max(f[i-1][j],f[i-1][j-v]+w,f[i-1][j-2v]+2w\dots ,f[i-1][j-sv]+sw)f[i][j]=max(f[i?1][j],f[i?1][j?v]+w,f[i?1][j?2v]+2w…,f[i?1][j?sv]+sw),發(fā)現(xiàn),倘若我們將原本的 f[i?1][j]f[i-1][j]f[i?1][j] 分組,組別如下
| 0 | f[i][0], f[i][v], f[i][2v],…,f[i][kb+1] |
| 1 | f[i][1], f[i][v+1], f[i][2v+1],…,f[i][kv+1] |
| …\dots… | ??\cdots \cdots?? |
| v-1 | f[i][v-1], f[i][v+v-1], f[i][2v+v-1],…,f[i][kv+v-1] |
不難發(fā)現(xiàn),只有組內(nèi)之間還是有遞歸關(guān)系的,組間不存在聯(lián)系,下面我們討論組內(nèi)是如何優(yōu)化的的
首先,我們假設(shè)當(dāng)前更新的 f[i][0…m] 為數(shù)組 dp[M], 上一層的 f[i-1][0…m]為數(shù)組pre[M]
有以下公式
dp[j] = pre[j]
dp[j+v] = max(pre[j] + w, pre[j+v])
dp[j+2v] = max(pre[j] + 2w, pre[j+v] + w, pre[j+2v])
dp[j+3v] = max(pre[j] + 3w, pre[j+v] + 2w, pre[j+2v] + w, pre[j+3v])
…
進(jìn)一步整理一下
dp[j] = pre[j]
dp[j+v] = max(pre[j], pre[j+v] - w) + w
dp[j+2v] = max(pre[j], pre[j+v] - w, pre[j+2v] - 2w) + 2w
dp[j+3v] = max(pre[j], pre[j+v] - w, pre[j+2v] - 2w, pre[j+3v] - 3w) + 3w
不難發(fā)現(xiàn),經(jīng)過這樣的整理,我們的 dp 數(shù)組就是在原數(shù)組基礎(chǔ)之上增加了一個(gè) 滑動(dòng)窗口求最大值的算法(單調(diào)隊(duì)列,應(yīng)該是遞減的)
所以說,對(duì)應(yīng)的代碼如下所示
#include <bits/stdc++.h> using namespace std;const int N = 1010, M = 20010; int f[M], pre[M], n, m; int v, w, s; int que[M];int main() {cin >> n >> m;for (int i = 1; i <= n; i ++ ) { // 對(duì)每個(gè)物品進(jìn)行遍歷scanf("%d%d%d", &v, &w, &s);memcpy(pre, f, sizeof f); // 將 i - 1 層的狀態(tài)保存起來,后面dp需要使用for (int j = 0; j < v; j ++ ) { // 對(duì)每一類進(jìn)行遍歷,j, j+v, j+2v,..static int head, tail;head = 0, tail = -1; // 循環(huán)隊(duì)列for (int k = j; k <= m; k += v) { // 對(duì)該類進(jìn)行 動(dòng)態(tài)規(guī)劃// 首先更新我們的滑動(dòng)窗口,去掉隊(duì)列前劃過去的,往后面加入if (head <= tail && k - que[head] > s * v) { // 隊(duì)列頭超過了窗口的最左側(cè)head ++;}while (head <= tail && (pre[que[tail]] - (que[tail] - j) / v * w) <= (pre[k] - (k - j) / v * w)) { // 用于保證窗口的單調(diào)性-- tail;}que[++ tail] = k; // 增加窗口的尾部f[k] = pre[que[head]] + (k - que[head]) / v * w; // 用窗口的維護(hù)數(shù)值來更新結(jié)果}}}cout << f[m] << endl;return 0; }八、慶功會(huì)
Acwing 鏈接
本題就是一個(gè)很裸的 多重背包問題
通過觀察復(fù)雜度,不難發(fā)現(xiàn), NMS 復(fù)雜度就可以過去
倘若空間不夠存儲(chǔ)的話,可以采取滾動(dòng)數(shù)組進(jìn)行優(yōu)化
空間優(yōu)化之后的代碼
#include <bits/stdc++.h> using namespace std;const int N = 510, M = 6010; int f[M]; int v, w, s, n, m;int main() {cin >> n >> m;for (int i = 1; i <= n; i ++ ) {scanf("%d%d%d", &v, &w, &s);for (int j = m; j >= 0; j -- ) { // 注意這個(gè)順序// f[j] = f[j];// f[i][j] = max(f[i-1][j], f[i-1][j-kv] + kw)for (int k = 1; k <= s; k ++ ) {if (j < k * v) break;f[j] = max(f[j], f[j - k * v] + k * w);}}}cout << f[m] << endl;return 0; }九、混合背包問題
Acwing 混合背包
這就是一個(gè) 01 背包,完全背包,多重背包的綜合
這里多重背包使用 二進(jìn)制優(yōu)化 即可跑完,復(fù)雜度O(NMlogS)O(NMlogS)O(NMlogS),時(shí)間復(fù)雜度沒有超時(shí)
下面給出 空間優(yōu)化的解題方法
十、二維費(fèi)用的背包問題
Acwing 題目鏈接
一個(gè)非常裸的 二維費(fèi)用 背包問題
十一、潛水員
Acwing 題目鏈接
首先,我先給出一個(gè)錯(cuò)誤的方法,然后說為什么是不可以的
該種方法 f[i][j][k]f[i][j][k]f[i][j][k] 表示對(duì)前 iii 個(gè)物品,選取氧氣量恰好為 jjj,氮?dú)饬壳『脼?span id="ozvdkddzhkzd" class="katex--inline">kkk,他所對(duì)應(yīng)的最小重量
初始化,顯然 memset(f, 0, sizeof f), f[0][0][0] = 1
對(duì)應(yīng)的代碼如下所示
但是該種方案是錯(cuò)誤的!
????原因在于,我們尋找的方案是 氧氣容量 大于等于 m, 氮?dú)馊萘看笥诘扔?n 的最消息重量的解決方案,**但是**上限是多少,我們是沒有辦法確定了 ????你可以理所當(dāng)然的以為 上限是 m * 2, n * 2, 或者是 m + M, n + N,但是都不對(duì) ????上限其實(shí)是 N * M 級(jí)別的,因?yàn)樘热粞鯕夂艹渥?#xff0c;但是氮?dú)饩褪呛苌?#xff0c;因?yàn)榈獨(dú)獠蛔阈枰粩嗉尤霘飧资沟玫獨(dú)膺_(dá)到標(biāo)準(zhǔn),與此同時(shí)氧氣也在增加,最壞情況下是 M * N ????因此,數(shù)組大小很爆炸,消耗時(shí)間也很爆炸,雖然遞歸式子沒問題,但是從時(shí)間和空間的角度來看,并不可行。
下面我們考慮另一種dp思路
f[i][j][k] 表示的是 前 i 個(gè)物品,氧氣量大于等于 j,氮?dú)饬看笥诘扔?k 的最小重量,同樣是從初始化和狀態(tài)轉(zhuǎn)移方程兩個(gè)方面進(jìn)行分析
初始化方面
f[0][x][y]=INF, 如果x>0∣∣y>0x>0 || y>0x>0∣∣y>0
f[0][x][y]=0,如果x<=0&&y<=0x<=0\&\&y<=0x<=0&&y<=0
其他都初始化為 INF
但是,因?yàn)槲覀兊臄?shù)組下表數(shù)值都為正數(shù),也就是說取不到這個(gè)負(fù)數(shù)的情況,因此后面的運(yùn)算對(duì)于負(fù)值的下標(biāo)我們是通過特判來選取數(shù)值的。
狀態(tài)轉(zhuǎn)移方程
對(duì)于遍歷第 i 個(gè)物品的屬性 a, b, c
f[i][j][k]=max(f[i-1][j][k], f[i-1][j-a][k-b]+v)
這個(gè)方程是很好理解的,但是關(guān)鍵是我們有的合法狀態(tài)是小于等于 0 的情況,使用數(shù)組是沒有辦法表示的(即使使用偏移量,也因?yàn)榭臻g過大,而不可行,類似于我們討論的第一種方法)
那么,我們是如何解決這個(gè)問題的呢?
對(duì)應(yīng)代碼如下所示
/*f[i][j]:表示恰到達(dá)到 i, j這個(gè)的時(shí)候, 最小重量注意是最小,初始化應(yīng)該為INF它需要的是M, 但是我們不能遍歷只是到M, N注意,如果是恰好的話,那么,范圍太大, 數(shù)組開不了,而且會(huì)超時(shí)但是這個(gè)數(shù)據(jù)開的 應(yīng)該要很大,很煩, 應(yīng)該是 [N*M][N*M]....因此我們應(yīng)該把這個(gè)狀態(tài)給改了,不適用這個(gè)f[i][j]:表示大于等于 i, j這個(gè)的時(shí)候, 最小重量f[-x][-y] = 0;others INF; */ #include <bits/stdc++.h> using namespace std;const int M = 30, N = 110, INF = 0x3f3f3f3f; int f[M * 2 + 5][N * 2 + 5]; int m, n, k1;int main() {cin >> m >> n;cin >> k1;memset(f, 0x3f, sizeof f);f[0][0] = 0;for (int i = 1; i <= k1; i ++ ) { static int a, b, c;scanf("%d%d%d", &a, &b, &c);for (int j = m; j >= 0; j -- ) {for (int k = n; k >= 0; k -- ) {if (j - a >= 0 && k - b >= 0) {f[j][k] = min(f[j][k], f[j - a][k - b] + c);} else if (j - a < 0 && k - b < 0) {f[j][k] = min(0 + c, f[j][k]);} else {f[j][k] = min(f[j][k], f[max(0, j - a)][max(0, k - b)] + c);}} }}cout << f[m][n] << endl;return 0; }十二、機(jī)器分配
ACWing Problem Link
挺簡(jiǎn)單個(gè)一個(gè)題目,該問題非 01 背包,也非完全背包、多重背包,因?yàn)殡m然是設(shè)備分配給同一個(gè)公司、或者是其他公司,增加的價(jià)值是不一樣的!
它更像是一個(gè)分組背包,f[i][j] i 表示的第 i 組,選取組內(nèi)的物品,組內(nèi)的物品可以使一個(gè)設(shè)備、兩個(gè)設(shè)備、?\cdots? 多個(gè)設(shè)備
f[i][j]f[i][j]f[i][j] 可以表示為前 i 個(gè)公司分配小于等于 j 個(gè)設(shè)備時(shí)候的最大價(jià)值
f[i][j]=maxf[i?1][j],f[i?1][j?1]+w[i][1],f[i?1][j?2]+w[i][2],?f[i?1][j?j]+w[i][j]f[i][j] = max{f[i-1][j], f[i-1][j-1]+w[i][1], f[i-1][j-2]+w[i][2], \cdot f[i-1][j-j]+w[i][j]}f[i][j]=maxf[i?1][j],f[i?1][j?1]+w[i][1],f[i?1][j?2]+w[i][2],?f[i?1][j?j]+w[i][j]
但是,因?yàn)樽詈笮枰敵龇峙涞姆桨?#xff0c;我們需要數(shù)組 g[i][j] 記錄一下到達(dá) f[i][j] 這個(gè)狀態(tài)時(shí)候,公司 i 分配了多少臺(tái)機(jī)器,之后就可以知道合法方案了
代碼如下
#include <bits/stdc++.h> using namespace std;const int N = 15, M = 20; int f[N][M], w[N][M]; int g[N][M], n, m;int main() {cin >> n >> m;for (int i = 1; i <= n; i ++ ) {for (int j = 1; j <= m; j ++ ) {scanf("%d", &w[i][j]);}}memset(f, 0, sizeof f);memset(g, 0, sizeof g);for (int i = 1; i <= n; i ++ ) {for (int j = 1; j <= m; j ++ ) {for (int k = 0; k <= j; k ++ ) {if (f[i][j] < f[i - 1][j - k] + w[i][k]) {f[i][j] = f[i - 1][j - k] + w[i][k];g[i][j] = k;}}}}// find the maxint ri, rj, res = -1;for (int j = 0; j <= m; j ++ ) {if (f[n][j] > res) {res = f[n][j];ri = n, rj = j;}}// find the pathvector<int> path;while (ri != 0) {path.push_back(g[ri][rj]);rj -= g[ri][rj];ri --;}reverse(path.begin(), path.end());cout << res << endl;for (int i = 0; i < path.size(); i ++ ) {printf("%d %d\n", i + 1, path[i]);}return 0; }十三、開心的金明
ACWing 題目鏈接
寫一個(gè)空間優(yōu)化的 01 背包即可
十四、有依賴的背包問題
ACWing Link
一個(gè)樹狀的有依賴背包問題,
f[u][j] 表示的是 u 作為根的子樹,當(dāng)需要的體積小于等于 j 的最大價(jià)值
顯然f[u][j] 的更新是依賴于他的兒子的,(因?yàn)槭菢?#xff0c;我們必須是dfs來確定便利的先后順序,而不是之間 for i = 1;i <= n; i ++了)
首先,我們先讓 父節(jié)點(diǎn)為 u 的點(diǎn)集合 set 跑一個(gè) 分組背包,得到 沒有加入 u 的 f[u][j],之后我們?cè)谌藶榈募尤?u 這個(gè)點(diǎn)就可以了,具體還可以優(yōu)化,請(qǐng)看下面的代碼.
十五、背包問題求方案數(shù)
Acwing Link
這個(gè)題目和 原本的背包問題類似,只不過是在維護(hù) 原本dp的數(shù)組過程中,多維護(hù)了一個(gè)數(shù)組, g[i][j] 用于表示到達(dá)當(dāng)前狀態(tài)的方案數(shù)
#include <bits/stdc++.h> using namespace std;const int N = 1010; const int MOD = 1e9 + 7; int v[N], w[N], n, m; int g[N][N], f[N][N];int main() {// inputcin >> n >> m;for (int i = 1; i <= n; i ++ ) {scanf("%d%d", &v[i], &w[i]);}// initialize, 注意這里的初始化可能和我們之前的初始化有些許的不同memset(f, 0, sizeof f);memset(g, 0, sizeof f);for (int i = 0; i <= m; i ++ ) {g[0][i] = 1;}// dpfor (int i = 1; i <= n; i ++ ) {for (int j = 0; j <= m; j ++ ) {if (j >= v[i]) {f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i]);g[i][j] = 0;// 更新方案數(shù)if (f[i][j] == f[i - 1][j]) {g[i][j] += g[i - 1][j];} if (f[i][j] == f[i - 1][j - v[i]] + w[i]) {g[i][j] += g[i - 1][j - v[i]];}} else {// 更新方案數(shù)f[i][j] = f[i - 1][j];g[i][j] = g[i - 1][j];}g[i][j] %= MOD;}}// outputcout << g[n][m] << endl;return 0; }十六、背包問題求具體方案
Problem Link
這個(gè)題目很有意思,我在這里進(jìn)行一下詳細(xì)的講解
對(duì)于原本的遞歸公式
????f[i][j]=max(f[i?1][j],f[i?1][j?v[i]]+w[i])f[i][j] = max(f[i-1][j], f[i - 1][j - v[i]] + w[i])f[i][j]=max(f[i?1][j],f[i?1][j?v[i]]+w[i]),f[i][j] 表示的是從 1…n 個(gè)物品選擇部分,使得體積為 j 的最大價(jià)值,使用 g[i][j] 可以表示當(dāng)前狀態(tài) f[i][j] 是從哪個(gè)狀態(tài)轉(zhuǎn)移來的。
????為了尋找方案的最小字典序,那我們必須要對(duì) g[i][j] 進(jìn)行處理,使其滿足這個(gè)最小字典序的要求,那么我們是讓 當(dāng)前物品 i 被選,還是不被選好呢?
????從直覺上來看,盡可能的讓當(dāng)前物品被選,可能 會(huì)好一些,這是因?yàn)槲覀?i 是從 1 -> n 開始枚舉的,當(dāng)然是需要被選中比較好。
????但是,不妨考慮一種情況,f[i][j] (假設(shè) i 為 7)倘若不選自己的話,為 1, 3, 5物品組合,倘若選了自己的話 為 2, 3, 7,顯然來看,這種貪心思路是有問題的。
????回到看看,問題到底是出在哪里了呢?因?yàn)?f[i][j] 指的是 1…n 個(gè)物品中選,對(duì)當(dāng)前物品 i 選不選進(jìn)行判斷,我們對(duì) 最后一個(gè)物品進(jìn)行優(yōu)先的選擇,然而最后一個(gè)物品,就是方案數(shù)中的最后一個(gè)數(shù),他是最不重要的,換而言之,我們應(yīng)當(dāng)將第一個(gè)物品,作為最重要的點(diǎn),最后判斷(因?yàn)樽詈鬀Q策的,才是最重要的)
????所以,不難得知,求字典序最大的,也是第一個(gè)數(shù)決策粒度對(duì)大,也應(yīng)該 放在最后一個(gè)進(jìn)行判斷。
為了適應(yīng)這種變化,我將 f[i][j] 數(shù)組的遞歸方程個(gè)真實(shí)含義做了改變
f[i][j] 表示從前 i … n 個(gè)物品中 選取部分,使得物品體積(代價(jià)) 小于等于 j 的最大價(jià)值
f[i][j] = max(f[i + 1][j], f[ i + 1][j - v[i]] + w[i]) 而且盡可能的使用后者,即保證能使用 i 物品就使用 i 物品(字典序最小)
十七、能量石
Problem Link
十八、今明的預(yù)算方案
總結(jié)
以上是生活随笔為你收集整理的动态规划问题之背包模型(18题)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SQL基础知识:DDL、DML、DQL
- 下一篇: springboot 多了8小时_日本人