动态规划——线性DP.1

动态规划算法一般用于求解具备某种最优性质的问题。算法

那它和贪心有区别吗?数组

固然有。否则叫动态规划干啥?优化

幼儿园英语老师: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问题,但愿你们好好理解这个问题,由于他真的狠重要!

 

今天的分享就到这里,咱们下次见。

相关文章
相关标签/搜索