動態規劃(簡稱DP)是算法設計思想當中最難也是最有趣的部分了,動態規劃適用於有重疊子問題和最優子結構性質的問題,是一種在數學、計算機科學和經濟學中常常使用的,經過把原問題分解爲相對簡單的子問題的方式求解複雜問題的方法。使用動態規劃方法解題有較高的時間效率,關鍵在於它減小了不少沒必要要的計算和重複計算的部分javascript
它的思想就是把一個大的問題進行拆分,細分紅一個個小的子問題,且可以從這些小的子問題的解當中推導出原問題的解。同時還須要知足如下兩個重要性質才能進行動態規劃java
最優子結構性: 既所拆分的子問題的解是最優解。算法
子問題重疊性質: 既在求解的過程中,每次產生的子問題並不老是新問題,有些子問題會被重複計算屢次。動態規劃算法正是利用了這種子問題的重疊性質,對每個子問題只計算一次,而後將其計算結果保存在一個表格中,當再次須要計算已經計算過的子問題時,只是在表格中簡單地查看一下結果,從而得到較高的解題效率數組
首先先引一道動態規劃的經典問題最長不降低子序列spa
它的定義是: 設有由n個不相同的整數組成的數列b[n],如有下標$i_1<i_2<···<i_L 且b[i_1]<b[i_2]<···<b[i_L]$$則稱存在一個長度爲L的不降低序列。設計
例如3d
13,7,9,16,38,24,37,18,44,19,21,22,63,15code
那麼就有13<16<38<44<63長度爲5的不降低子序列。
可是通過觀察實際上還有7<9<16<18<19<21<22<63長度爲8的不降低子序列,那麼是否還有更長的不降低子序列呢?請找出最長的不降低子序列blog
輸入格式ip
第一行爲n,表示n個數(n<=100000),第二行爲n個數的數值(數字之間用空格隔開且最後一個數字末尾不能留有空格)
輸出格式
一個整數,表示最長不降低子序列的長度。
輸入例子
4
1 3 1 2
輸出例子
2
思路
假如要求得某一段的最優,就要想更小段的最優怎麼求,再看看由最小段的最優可否擴大推廣到最大段的最優;
假設這麼一個表:
第三行表示該序列元素的所能鏈接的最長不降低子序列的長度,由於自己長度爲1,因此初始值都爲1
第四行表示連接於哪一個序列元素造成最長不降低子序列
先從倒數第二項63算起,在它的後面僅有一項,所以僅做一次比較,由於63>15,因此從63出發,不做任何連接,長度仍是爲1。
再看倒數第三項22,在它的後面有2項,所以必需要在這2項當中找出比22大,長度又是最長的數值做爲連接,因爲只有22<63,因此修改22的長度爲2,即自身長度加上所連接數值的長度,並修改連接位置爲13,也就是63的下標。
再看倒數第四項21,在它的後面有3項,所以必需要在這3項當中找出比21大,長度又是最長的數值做爲連接(注意:是長度),很容易看出,數值22知足該條件,所以,修改21的長度爲3,並修改連接位置爲12,即22的序列下標。
依次類推,最後結果以下表:
最終狀態的轉移方程式爲: $f(i) = max {f(j)} +1 (b_j<b_i 且 i<j)$.時間複雜度爲$O(n^2)$
代碼
process.stdin.setEncoding('utf8'); var arr = []; var bool = 0; var n = 0; var longest = 1; var a = []; var dp = []; process.stdin.on('readable', function() { var chunk = process.stdin.read(); if (chunk !== null) { arr.push(chunk.trim()) } if(bool>=2){ n = arr[0]; process.stdin.emit('end') } bool++ }); process.stdin.on('end', function() { a = arr.slice(1).join(" ").split(" ").map(function(index, elem) { return parseInt(index); }) for(let i = 0;i<n;i++){ dp[i] = 1; } for(let i = 1;i<n;i++){ for(let j = 0;j<i;j++){ if(a[i]>a[j]){ dp[i] = Math.max(dp[j]+1,dp[i]) } longest = Math.max(dp[i],longest) } } console.log("最長長度爲:"+longest); process.stdout.write('end'); });