動態規劃算法一般用於求解具備某種最優性質的問題。算法
那它和貪心有區別嗎?數組
固然有。否則叫動態規劃幹啥?優化
幼兒園英語老師:DP是啥?spa
小盆友:Dog&Peppa pigcode
英語老斯:恩恩!真聰明!blog
然而,你是小盆友嗎?ci
若是是io
若是不是,class
DP是D****** P*******的縮寫。原理
意思是動態規劃。
聰明的bolt告訴你:是Dynamic Programming的縮寫!!!
動態規劃注重 表示狀態,轉移狀態
so
講一個栗子:
LIS:
最長上升子序列
這是線性動態規劃中最經典的栗子之一。
最長上升子序列(Longest Increasing Subsequence,LIS),指一個序列中最長的單調遞增的子序列。
注意不是子串,因此能夠不相鄰。
好比說:
序列:3 2 1 4 6 8 7 9
它的LIS是5
3 4 6 8 9
或3 4 6 7 9
或2 4 6 8 9
······
還有不少種狀況。
因而咱們珂以得出:
動態規劃的最優解,有不一樣的組合狀況,但答案只有一個。
因此,若是NOIP出了動態規劃的題目時,通常會叫你求值,而不是求狀況。
這是好處!
BUT,有的老師不會好心,會給更多限制條件,使Ans只有一種狀況,那就更有難度了。
LIS問題要用動態規劃作。
方法一:
這是一個好理解的方法。
可是更好使耗時
不難看出,dp [ i ]就是第 i 個數的LIS
那代碼怎麼實現的呢?
先別急,咱們在舉個生活中的栗子。
老師要你算1+2+3+4+5+6+7+8+9=?時,你會算得45,
老師再問你1+2+3+4+5+6+7+8+9+10=?時,你是會用1+···+10,仍是用以前算的45+10?
聰明人會用後面一種。
因此,咱們根據這個方便的原理,發現我每次計算dp [ i ] 時,若是用到了前面的 dp 值,則會減小必定的計算量。
在咱們每次枚舉一個數的dp值時,只要掃描在它前面比它小的數,那些比他小的數的dp值的最大值+1就是所求數的dp值
由於比所求數小的數的dp值表示它的LIS,再來一個比它大的數,大樹數的LIS就等於小數的LIS+1.
但因爲小數的LIS有大有小,咱們又要求最長子序列,咱們就要取最大值。
一番思考後,咱們找到了狀態轉移方程,也就是動態規劃中最重要的東西:
對於每個 i ,咱們枚舉它前面的數 j,if (i > 它前面的數 j ) dp [ i ] = max ( dp [ i ] , dp [ j ] + 1 ) ;
這個算法的時間複雜度是O(n^2)的,慎用。
code:
1 int n,a[1001]/*用來存序列*/,dp[1001]/*dp值*/;//數組大小根據題目而定。 2 cin>>n; 3 dp[1]=1; //1的dp值爲1 4 for(int i=1;i<=n;i++) 5 cin>>a[i]; 6 for(int i=1;i<=n;i++) 7 { 8 for(int j=1;j<i;j++) 9 { 10 if(a[i]>a[j]) 11 { 12 dp[i]=max(dp[i],dp[j]+1); //狀態轉移 13 } 14 } 15 } 16 cout<<dp[n]<<endl;
注意要初始化dp [ 1 ] = 1.剩下的爲 0.
還有另外一種時間複雜度爲 n log n 的LIS算法
看,栗子!
2 1 5 3 6 4 6 3
在 dp 值相同的狀況下,保留較小的數顯然更好。由於後面的數若能跟較大的數構成上升子序列,也必定能能較小的數構成上升子序列,反之則不必定。例如 a_3=5 與 a_4=3 的 dp 均爲 2,但 a_6=4 不能與 a_3=5 構成上升子序列,而能夠和 a_4=3 構成上升子序列。 所以,不一樣的 dp 值只須要存一個對應的最小值,將這個最小值順序排列,他們必定是升序(嚴格來講是不降低)的。 因而,藉助二分查找的方式,就能夠很快查到更新的值,總體時間複雜度 O(nlogn)。
這個就是上面的一個優化,也沒有太多可講的,本身打一遍代碼也就熟了。
code:
1 const int maxn=1e5+5; 2 int a[maxn]; 3 int n; 4 int dp[maxn]; 5 int ans=1; 6 int find(int x){ 7 int l=1,r=ans,m; 8 while(l<r){ 9 m=l+(r-l)/2; 10 if(dp[m]>=a[x]){ 11 r=m; 12 } 13 else{ 14 l=m+1; 15 } 16 } 17 return l; 18 }//二分查找 19 int main(){ 20 scanf("%d",&n); 21 for(int i=1;i<=n;i++)scanf("%d",&a[i]); 22 dp[1]=a[1]; 23 for(int i=2;i<=n;i++){ 24 if(a[i]>dp[ans]){ 25 dp[++ans]=a[i]; 26 } 27 else{ 28 int pos=find(i); 29 dp[pos]=a[i]; 30 } 31 } 32 printf("%d",ans); 33 return 0; 34 }
這就是LIS問題,但願你們好好理解這個問題,由於他真的狠重要!
今天的分享就到這裏,咱們下次見。