動態規劃演算法一直是面試手撕演算法中比較有挑戰的一種型別。很多的分配問題或者排程問題實際上都可能用動態規劃進行解決。(當然,如果問題的規模較大,有時候會抽象模型使用動歸來解決,有時候則可以通過不斷迭代的概率演算法解決查詢次優解)
所以,動歸很重要,至少演算法思想很重要。
例題通過把原問題分解為相對簡單的子問題的方式求解複雜問題的方法。動態規劃常常適用於有重疊子問題和最優子結構性質的問題。
最優子結構:當問題的最優解包含了其子問題的最優解時,稱該問題具有最優子結構性質。
重疊子問題:在用遞迴演算法自頂向下解問題時,每次產生的子問題並不總是新問題,有些子問題被反覆計算多次。動態規劃演算法正是利用了這種子問題的重疊性質,對每乙個子問題只解一次,而後將其解儲存在乙個**中,在以後盡可能多地利用這些子問題的解。不理解不用怕,結合後面題目來理解這些概念。這些概念完全是已經會動歸的人來總結出來的,所以先理解動歸,然後再來看這些文縐縐的概括。
共同點:
二者都要求原問題具有最優子結構性質,都是將原問題分而治之,分解成若干個規模較小(小到很容易解決)的子問題。然後將子問題的解合併,形成原問題的解。
不同點:
根據問題,找到【最優子結構】。把原問題從大化小的第一步,找到比當前問題要小一號的最好的結果,而一般情況下當前問題可以由最優子結構進行表示。
確定問題的【邊界】。根據上述的最優子結構,一步一步從大化小,最終可以得到最小的,可以一眼看出答案的最優子結構,也就是邊界。
通過上述兩步,通過分析最優子結構與最終問題之間的關係,我們可以得到【狀態轉移方程】。
所有的動態規劃問題都可以通過多層巢狀迴圈遍歷所有的可能,將符合條件的個數統計起來。只是時間複雜度是指數級的,所以不推薦。
遞迴的時間複雜度是由遞迴層數和最優子結構的個數決定的。
在爬階梯問題,最少找零錢問題中,遞迴的時間複雜度和空間複雜度都比動歸方法的差,但是在國王與金礦的問題中,不同的資料規模,動歸方法的時間複雜度和空間複雜度不一定比遞迴的要好。所以具體問題具體分析。
在階梯數n比較多的時候,遞迴演算法的缺點就顯露出來了:時間複雜度很高。如果畫出遞迴圖(像二叉樹一樣),會發現有很多很多重複的節點。然而傳統的遞迴演算法並不能識別節點是不是重複的,只要不到終止條件,它就會一直遞迴下去。
為了避免上述情況,使遞迴演算法能夠不重複遞迴,就把已經得到的節點都存起來,下次再遇到的時候,直接用存起來的結果就行了。這就是備忘錄演算法。
備忘錄演算法的時間複雜度和空間複雜度都得到了簡化。
上述的備忘錄演算法,儘管已經不錯了,但是依然還是從原問題,遍歷得到所有的最小子問題,空間複雜度是o(n)。
為了再次縮小空間複雜度,我們可以自底向上的構造遞迴問題,通過分析最優子結構與最終問題之間的關係,我們可以得到【狀態轉移方程】。
然後從最小的問題不斷往上迭代,即使一直迭代到最大的原問題,也是只依賴於前面的幾個最優子結構。這樣,空間複雜度就大大簡化。也就得到了動歸演算法演算法。
例1: climbing stairs(爬樓梯問題)
leetcode原題:你正在爬乙個有n個台階的樓梯,每次只能上1個或者2個台階,那麼到達頂端共有多少種不同的方法?
建立模型:
問題求解:
class solution else
}}
遞迴的時間複雜度是由遞迴層數和最優子結構的個數決定的。這裡的階梯數是 n ,最優子結構個數是2。如果想象成乙個二叉樹,那麼就可以認為是乙個高度為n-1,節點個數接近2的n-1次方的樹,因此此方法的時間複雜度可以近似的看作是o(2n) 。
class solution else if (map.containskey(n)) else
}}
class solution
// 邊界條件
int a = 1;
int b = 2;
int result = 0;
// 最優子結構與最終問題之間的關係
for (int i = 3; i <= n; i++)
return result;
}}
空間複雜度o(1), 時間複雜度o(n)例2: ****** change using the fewest coins(最少找零錢問題)
google面試題:假設你是一家自動售貨機製造商的程式設計師。你的公司正設法在每一筆交易 找零時都能提供最少數目的硬幣以便工作能更加簡單。已知硬幣有四種(1美分,5美分,10美分,25美分)。假設乙個顧客投了1美元來購買37美分的物品 ,你用來找零的硬幣的最小數量是多少?
建立模型:
問題求解:
class solution
};int getfewestcoins(int n)
if (coinset.contains(n))
int mincoins = n;
int numcoins = integer.max_value;
for (int coin : coinset)
// 更新最小值
if (numcoins < mincoins)
}return mincoins;
}}
class solution
};int getfewestcoins(int n)
for (int cent : coinset)
}list[i] = collections.min(subcal);
subcal.clear();
}return list[n];
}}
快速入門演算法 動態規劃
動態規劃問題的一般形式是 最值問題 如 最長遞增子串行 最小距離等等 那麼計算機如何求解動態規劃問題,其實就只有一種方法 窮舉法 沒錯,就是暴力的窮舉出所有情況,然後在所有答案中找到乙個最優的解,這就是動態規劃最基本的思想。但是窮舉是基本思想,動態規劃還有其獨特之處,首先動態規劃這類問題存在 重疊子...
動態規劃入門
1 用 dp 做的題大多數返回值是int boolean,求max min,不能打亂原來輸入順序。2 動態規劃有兩個重要定義,乙個叫 optimal substructure 另乙個叫 overlap subproblem 各種排序 tree 類問題中,都會用到 divide conquer 的思想...
動態規劃入門
大家可以看看這篇文章dp,哪個更容易理解就看哪個!一 動態規劃的定義 動態規劃程式設計是一種針對於解決最優化問題的一種途徑 一種方法,而不是一種特殊演算法,也就是說它沒有固定的模板。在動態規劃中,每走一步都要看看能不能最優,而且動態規劃最擅長的就是多階段問題!二 動態規劃的基本概和基本模型構成 1....