例四:扔雞蛋問題
定義雞蛋的硬度為 k,則代表雞蛋最高從 k 樓扔下來不會碎掉,現在給你 n 個硬度相同的雞蛋,樓高為 m,問最壞情況下最少測多少次,可以測出雞蛋的硬度。
我們假設有2個雞蛋,100層樓。那麼我們應該如何扔在最壞情況下測的次數最少呢?
1.二分法:剛開始我傻傻的以為是二分法,第乙個雞蛋在第50層扔,但是如果乙個雞蛋在第50層碎了,那麼第二個雞蛋只能從第1層扔直到第49層,才能測出雞蛋的硬度(在最壞情況為49層時),這樣最壞情況下需要測50次
2.平方根法:我們繼續優化,我們每10層測一次,第10層若碎了,第二個雞蛋就從1層測到9層。若沒碎繼續測第20層…以此類推,最壞情況是第99層時,這時我們需要測10 + 9 = 19次
3.假設法:方法二一定是最優解嗎?我們可以假設一下,設2個雞蛋100層樓在最壞情況下最多測x次,那麼第乙個雞蛋應該放在第x層最合適(如果第乙個雞蛋放在是x + i層,如果碎了最壞情況下最多測x + i次,比x次多;如果第乙個雞蛋放在x - i層,如果沒碎的話,由於x - i比x小,所以它剩餘測的次數一定 >= x)
因此,我們第一次放在x層(碎了話最多測x次,沒碎的話第二次放在2 * x - 1層,碎的話最多測x次,沒碎的話第三次放在3 * x - 3層,碎的話最多測x次,沒碎的話第四次放在4 * x - 6層…依次類推),我們只要保證x + (x - 1) + (x - 2) … + 1 >= 100即可,這樣解出最優答案為14
好了,以上是推理,我們可以用dp的方法來求出正確的答案。我們可以假設dp[i][j]
代表i個雞蛋j層樓最少測多少次。這樣我們可以得到:
初始化:
dp[1][j] = k (1 <= j <= m) //乙個雞蛋j層樓最壞情況下最少需要測j次
dp[i][1] = 1 (1 <= i <= n) //i個雞蛋1層樓最壞情況下最少需要測1次
這樣我們可以得到dp轉移式:
dp[i][m] = min(max(dp[i - 1][j - 1], dp[i][m - j])) (1 <= j <= m)
那麼,當n範圍為1e2,m範圍為1e9的時候,我們怎麼辦呢?
陣列肯定開不下那麼大,而且上面演算法的時間複雜度為o(n(m^2)),會直接爆掉的。這時我們就可以進行重新定義狀態資訊來優化演算法。
我們可以假設dp[i][j]
代表i個雞蛋測j次最多能測多少層樓。這樣我們可以得到:
//初始化:
dp[1][j] = j //乙個雞蛋測j次最多可以測j層樓
dp[i][1] = 1 //i個雞蛋測1次最多可以測1層樓
我們假設第i個雞蛋測k次最多測n層樓,那麼在測n層樓的情況下,第一次需要測的位置應該是i-1個雞蛋測k - 1次最多測的層數+1(只有滿足這個條件,在第m層碎了測的次數最壞情況才為k)
若在第m層沒碎,那麼往上最多能測的層數應該是i個雞蛋測k - 1次最多的層數
因此,dp[i][k] = dp[i - 1][k - 1] + dp[i][k - 1] + 1
我們只要找第乙個dp[n][j]大於m的j即是正確答案
最後上ac**:
#include #define ll long long
ll dp[35][100005] = ;
int main () else
for (int i = 1; i <= n; i++)
for (int i = 2; i <= n; i++)
}for (int j = 1; j <= 100000; j++) }}
return 0;
}
如果有寫的不對或者不全面的地方 可通過主頁的****進行指正,謝謝
動態規劃5
名名的媽媽從外地出差回來,帶了一盒好吃又精美的巧克力給名名 盒內共有 n 塊巧克力,20 n 0 媽媽告訴名名每天可以吃一塊或者兩塊巧克力。假設名名每天都吃巧克力,問名名共有多少種不同的吃完巧克力的方案。例如 如果n 1,則名名第1天就吃掉它,共有1種方案 如果n 2,則名名可以第1天吃1塊,第2天...
動態規劃 斜率優化
一 引用 一般dp 方程可以轉化成 dp i f j x i 的形式,其中 f j 中儲存了只與 j相關的量。這樣的 dp方程可以用單調佇列進行優化,從而使得 o n 2 的複雜度降到 o n 可是並不是所有的方程都可以轉化成上面的形式,舉個例子 dp i dp j x i x j x i x j ...
動態規劃優化方法
之前我們學習過動態規劃方法,但是並沒有對dp進行系統細緻的優化。今天來看一下dp的優化方法。做變換 等式就可以看作一條直線,每個j都確定出乙個點 x,y 對於當前要求解的i,其斜率已確定 且隨i單增 要使dp i 最大即要使這條直線過某乙個j形成的點並使得的截距最小。假設之前已處理完前9個點,將他們...