最长递增子序列的问题----两个经典题目:合唱队和Redraiment走法(简单动态规划)

来源:互联网 发布:seo 关键词掉排名 编辑:程序博客网 时间:2024/05/16 06:49

动态规划:将递归算法重新写成非递归算法,让后者把那些子问题的答案系统的记录在一个表内,利用这种方法的技巧叫做动态规划。

此处黑人问号?????????

概念太抽象,还是具体上一点动态规划的例子吧


例1:计算斐波那契数:f(n) = f(n-1) + f(n-2) , f(0)=1 , f(1)=1 

根据递推公式,我们可以很容易的写出计算斐波那契数的递归函数程序:

int Fibonacci( int N ){

if(N<=1)

return 1;

else

return Fibonacci(N-1)+Fibonacci(N-2);

}

                      程序写完很开心,可是编译运行后你会发现,此处黑人问号, what??????

很不幸,超   时,

怎么会酱紫呢?查了半天,你会发现,简单的去模仿递归算法,效率灰常的低,比如f(4)=f(3)+f(2),f(3)=f(2)+f(1),重复计算了f(2),而且随着N的增大,重复计算的规模将达到恐怖级别

那怎么办呢?好办,就是预先用一个表来保存已经算出的值,对已经解过的子问题不再进行递归调用,那这样就可以避免爆炸增长了

现在用迭代 替代 递归重新写一下以上程序:

int Fibonacci ( int N ){

int i=,last,NextToLast,Answer;

if(N<=1)

return 1;

last=NextLast=1;

for(i=2;i<=N;++i){

Answer=last+NextToLast;

NextToLast=Last;

last=Answer;

}

return Answer;

}

            动态规划好用吧,直接将时间复杂度降低到了O(n)


例2:常敲代码手不软的博客,教你彻底学会动态规划入门篇,由于不能转载,此处放一下链接,http://blog.csdn.net/baidu_28312631/article/details/47418773,讲的非常的好,由浅入深讲解动态规划


例3:依然是常敲代码手不软的博客,教你彻底学会动态规划进阶篇,由于不能转载,此处放一下链接,http://blog.csdn.net/baidu_28312631/article/details/47426445


例4:就是以下两个最长递增子序列的问题


下面这两个题目归根结底都是求最长递增子序列问题,本质解法都是动态规划:

一、问题描述

题目一:

题目描述


计算最少出列多少位同学,使得剩下的同学排成合唱队形


说明:


N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形。 合唱队形是指这样的一种队形:设K位同学从左到右依


次编号为1,2…,K,他们的身高分别为T1,T2,…,TK,   则他们的身高满足存在i(1<=i<=K)使得T1<T2<......<Ti-1<Ti>Ti+1>......>TK。 你的任


务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形


题目二:


题目描述 


   Redraiment是走梅花桩的高手。Redraiment总是起点不限,从前到后,往高的桩子走,但走的步数最多,不知道


为什么?你能替Redraiment研究他最多走的步数吗? 

 

样例输入

6

2 5 1 5 4 5

 

样例输出

3

 

提示


Example: 


6个点的高度各为 2 5 1 5 4 5 


如从第1格开始走,最多为3步, 2 4 5 


从第2格开始走,最多只有1步,5 


而从第3格开始走最多有3步,1 4 5 


从第5格开始走最多有2步,4 5


所以这个结果是3


二、问题解决
题目一解题思路:
步骤一:根据题目二中的解题思路,数组data正向求出辅助数组dp1;
步骤二:使用库函数reverse,将数组data反向,然后求出辅助数组dp2;
步骤三:对于正向数组data中的每一个数,设置一个数组dp=dp1[i]+dp2[n-1-i];表明数组中第i个数在正向最长子序列中的位置,以及反向最长子序列中的位置,而且必须注意:该数本身被重复计算了一次
步骤四:根据步骤三,令dp最大值等于max_value,就可以计算出合唱队抛物线队伍,题目要求计算出队人数,所以用n-max_value+1最终需要出队人数。


题目二解题思路:
步骤一:如果存在一个数组data,设置一个同样大小的辅助数组dp,dp为一维数组,并将其所有值初始化为1。例如:data[i],dp[i]=1,表示data[i]为某个最长子序列中的第1个数字,data[i],dp[i]=2,表示data[i]为某个最长子序列中的第二个数字;
步骤二:然后判断该数字之前的数字data[j]和data[i]的大小(j<i),若data[j]<data[i],则dp[i]的值就要变化了,因为它将不再是某个最长子序列的第一个数字;dp[i]=max(dp[i],dp[ j ]+1),要么dp[i]本身较大,要么在dp[j]前提下,加1;
步骤三:根据步骤二,比较数组dp[i]中的值,找出最大值,即会得到最长子序列的长度。










阅读全文
0 0