01揹包
問題:若干個物體,每個價值為c[i]重量為w[i],數量均為1,揹包最大容量為w,問怎樣取物體才能最大化收益?
解法:dp[i][j]以j為容量為放入前i個物品(按i從小到大的順序)的最大價值。有遞推關係式:dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+c[i])。這裡的第一維度可以省略。
注意這裡的記憶體重量迴圈j為w到0,遞減關係(保證每個物體取一遍)(後面的不會修改前面的)
當揹包空間j少於當前物體的重量時不選該物體這一步可以省略,對後續沒有影響。
最終寫法為:
1for(int i=1; i<=m; i++) //
物品 2
for(int j=w; j>=0; j--) //
容量 3
空間優化:
1for(i=1;i<=m;i++)
6 }
完全揹包
問題:若干個物體,每個價值為c[i]重量為w[i],數量均為無限,揹包最大容量為w,問怎樣取物體才能最大化收益?
解法:dp[i][j]以j為容量為放入前i個物品(按i從小到大的順序)的最大價值。有遞推關係式:dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+c[i])。這裡的第一維度可以省略。
注意這裡的記憶體重量迴圈j為w[i]到w,遞增關係(保證每個物體取一遍)(後面的不會修改前面的)
最終寫法為:
1for(int i=1; i<=n; i++)
2for(int j=w[i]; j<=w; j++)//
注意此處,與0-1揹包不同,這裡為順序,0-1揹包為逆序
3 f[j]=max(f[j],f[j-w[i]]+c[i]);
例題:poj2229
題意:找一些2^x(0<=x),使它們的和為n。比如,n=7:
1) 1+1+1+1+1+1+1
2) 1+1+1+1+1+2
3) 1+1+1+2+2
4) 1+1+1+4
5) 1+2+2+2
6) 1+2+4
(1 <= n <= 1,000,000).
解法:直接完全揹包即可,注意關係式更新的時候為「拿」的情況加上「不拿」的情況;注意初始化dp[0]=1
1intn;
2int num[25];3
int dp[1000000 + 5];4
intmain() 14}
15 printf("
%d\n
", dp[n]);
16return0;
17 }
多重揹包
多重揹包裡面每個物品的數量是有限的,即k個,因此基本的求解思路是將其轉換為01揹包再求解;或者列舉k的個數。
v[i],w[i],與c[i],分別表示這種物品的體積、價值和件數。
1for (int i = 1; i <= n; i++)
2for (int j = m; j >= c[i]; j--)
3for (int k = 0; k <= num[i]; k++)
4if (j - c[i] * k >= 0
)5 f[j] = max(f[j], f[j - c[i] * k] + v[i] * k);
當然直接這樣做一定會超時,其複雜度為$o(v\sum_{}^{}n[i])$。
優化方法第一種是改變分割方法(二進位制),即:將第 i 種物品分成若干件物品,其中每件物品有乙個係數,這件物品的費用和價值均是原來的費用和價值乘以這個係數。使這些係數分別為$1 , 2 , 4 ,..., 2^, n[i]-2^+1$,且k 是滿足 n[i] - 2^+1>0 的最大整數。例如,如果 n[i] 為13,就將這種物品分成係數分別為 1 , 2 , 4 , 6 的四件物品。
分成的這幾件物品的係數和為 n[i],表明不可能取多於 n[i] 件的第 i 種物品。另外這種方法也能保證對於 0 .. n[i] 間的每乙個整數,均可以用若干個係數的和表示,這個證明可以分 0.. 2^-1 和 2^...n[i] 兩段來分別討論得出。
這樣就將第i種物品分成了 o(logn[i]) 種物品,將原問題轉化為了複雜度為 o(v*∑log n[i])的01揹包問題。
1for (int i = 1; i <= n; i++)
10if (z > 0
) 14}15
for (int i = 1; i <= tot; i++)
16for (int j = m; j >= c[i]; j--)
17 f[j] = max(f[j], f[j - c[i]] + v[i]);
優化方法第二種是採用優先佇列的方法,推導方法及其神奇,看不懂了,掛個鏈結
複雜度為 o(n∗v)
1for(int i=1;i<=n;i++)//
列舉物品種類 221
}22 }
例題:poj1742
題意:有 n 種面額的硬幣,面額個數分別為v[i]、num[i],求最多能搭配出幾種不超過 m 的金額?
解法:沒有時間、空間優化都會炸。所以採用多重揹包加上規則定義求解,定義如下:
①首先來看看樸素的方法:
bool dp[i][j] := 用前 i 種硬幣能否湊成j
遞推關係式:
dp[i][j] = (存在 k 使得 dp[i – 1][j – k * v[i]] 為真,0 <= k <= c_i 下標合法)
然後三重迴圈 i j k 遞推
這樣的話複雜度為o(m*σc_i),肯定過不去。。
②然後我們想到將問題轉化為01揹包,並利用二進位制(具體可以看揹包九講)來優化
複雜度為o(m*σlogc_i),仍然tle。。
③我們優化dp的狀態:
狀態:dp[i][j] : = 用前 i 種硬幣湊成 j 時第 i 種硬幣最多能剩餘多少個( - 1表示配不出來)
轉移:①若 $dp[i-1][j]>=0$,即前 $i-1$ 種可以配成 $j$,所以根本用不到第$i$種,所以剩餘$nun[i]$種 $dp[i][j]=nun[i]$
②若$j③其他情況,由於$a[i]$還有剩,所以$dp[i][j]$相當於在$dp[i][j-a[i]]$的基礎上多使用了乙個$a[i]$,此時 $ dp[i][j]=dp[i][j-a[i]]-1$
1int v[106], num[106];2
intdp[maxn];
3int
n, m;
4int
main()
22int a = 1
;23 cout << ans <2526
return0;
27 }
揹包問題 01揹包問題
n個物品,總體積是v,每個物品的體積的vi,每個物品的最大價值是wi,在不超過v的體積下求最大價值 eg揹包容積為 5 物品數量為 4 物品的體積分別為 物品的價值分別為 思路定義乙個二位陣列int f new int n 1 v 1 f i j 就表示在1 i個物品中選取體積小於v的情況的最大價值...
01揹包問題 完全揹包問題 多重揹包問題
0 1 揹包問題 給定 n 種物品和乙個容量為 c 的揹包,物品 i 的重量是 wi,其價值為 vi 問 應該如何選擇裝入揹包的物品,使得裝入揹包中的物品的總價值最大?分析一波,面對每個物品,我們只有選擇拿取或者不拿兩種選擇,不能選擇裝入某物品的一部分,也不能裝入同一物品多次。解決辦法 宣告乙個 大...
01揹包問題 完全揹包問題 多重揹包問題
有n件物品和乙個容量為v 的揹包。放入第i件物品耗費的空間是ci,得到 的價值是wi。求解將哪些物品裝入揹包可使價值總和最大。這是最基礎的揹包問題,特點是 每種物品僅有一件,可以選擇放或不 放。用子問題定義狀態 即f i,v 表示前i件物品恰放入乙個容量為v的揹包可以 獲得的最大價值。則其狀態轉移方...