經典動態規劃——揹包問題系列一

經典動態規劃——揹包問題系列一

複賽前發一波博客,雖然意義不是很大了……數組

本篇講的是揹包問題基礎優化

01揹包問題

簡述

有N件物品和一個容量爲V的揹包。第i件物品的體積是c[i],價值是w[i]。求解將哪些物品裝入揹包可以使這些物品的費用總和不超過揹包容量,且價值總和最大。spa

思路

動態規劃的基本題,揹包問題之母。code

對動態規劃有必定了解的人應該都應理解它的原理和方程。博客

所謂動態規劃,就是把問題分紅互相聯繫的多個階段決策,每一步決策都能影響答案。固然,各個階段決策的選取不是任意肯定的,它依賴於當前面臨的狀態,又影響之後的發展。那麼咱們須要肯定,各階段之間的關係是什麼以及各狀態如何決策才能使答案最優。class

在揹包問題中,咱們一般把揹包容量設爲狀態。那麼咱們須要決策,到底如何放置物品,才能使各階段揹包在有限容量內裝價值最多的物品?基礎

首先咱們須要知道:揹包容量大的狀態必定由容量小的狀態轉移過來,由於咱們要不斷選物品,這樣是總重量愈來愈大。如今咱們用f[i][j]示前i件物品恰放入一個容量爲v的揹包能夠得到的最大價值。原理

咱們只要枚舉物品,在有限的空間裏取物品,並一直取max,就能在規定揹包空間內跑完。方法

\[ dp[i][v] = max(dp[i-1][v],dp[i-1][v-c[i]]+w[i]) \]di

爲何要逆着枚舉?由於容量大的狀態要從容量小的狀態轉移過來,而咱們要固定每一個容量去裝物品,因此要從大到小枚舉。

代碼

for(int i=1;i<=n;i++) 
    for(int j=v;j>=0;j--)
    {
        if(j >= c[i])
            dp[i][j]=max(dp[i-1][j-c[i]]+w[i],dp[i-1][j]);
        else
            dp[i][j] = dp[i-1][j];
    }

優化

將咱們能夠優化一維空間,方程就變成了:
\[ dp[v] = max(dp[v],dp[v-c[i]] + w[i]) \]

代碼

for(int j=v;j>=w[i];j--)
    dp[j] = max(dp[j],dp[j-c[i]]+w[i]);

拓展:揹包方案數問題

簡述

仍是01揹包,如今讓你求取得物品總價值最大時的方案數。

思路

相似地,仍是思考狀態是什麼以及如何從上一個狀態轉移過來。咱們還設狀態爲前i個物品,以及揹包體積v。動歸數組存的是方案數,而後思考如何轉移。

咱們設dp[i][j]存的是最大價值,f[i][j]存的是方案數。

類比以前的方程,若dp[i][j]由dp[i-1][j]轉移過來,那麼f[i][j]的應該等於f[i-1][j];若dp[i][j]由dp[i][j-c[i]]轉移過來,那麼f[i][j]也應該等於f[i][j-c[i]];若dp[i][j-c[i]]與dp[i-1][j]相等,那麼根據加法原理,f[i][j]應是f[i-1][j]與f[i][j-c[i]]的和。

因此咱們得出了方程:
\[ f[i][j] = \begin{cases} f[i-1][j]\ \ \ \ \ \ (dp[i-1][j] > dp[i][j-c[i]])\\ f[i][j-c[i]]\ \ \ (dp[i-1][j] < dp[i][j-c[i])\\ f[i-1][j] + f[i][j-c[i]]\ \ (dp[i-1][j] = dp[i][j-c[i]) \end{cases} \]

拓展:數字組合問題

簡述

給你n塊錢,有m種錢幣,每種錢幣只能用一次,問組成n塊錢有多少種方法。

思路

既然你要考慮用dp作,那你確定會毫無疑問地去選擇前i個物品,選j個和錢數k做爲狀態,用揹包來計算方案數。

也就是
\[ dp[i][j][k] += dp[i-1][j][k] \]

\[ if(k >= v[i]) \\ dp[i][j][k] += dp[i-1][j-1][k-v[i]] \]

事實上這樣會超時。

因此要怎麼作?

先將物品排升序,枚舉狀態\(i\),表示第\(i\)個物品是未被選的物品中價值最小的一個物品。

那麼價值比它小的物品,都在它左面,並且都會被選,把他們用前綴和維護起來。

如今你剩餘一些錢j,從後面選取一些物品使價值最大但不超過j,這就是揹包。

因此一共二維,時間複雜度空間複雜度皆下降。

相關文章
相關標籤/搜索