最长下降子序列O(n^2)及O(n*log(n))解法

求最长下降子序列和LIS基本思路是完全一样的,都是很经典的DP题目。

问题大都类似于 有一个序列 a1,a2,a3...ak..an,求其最长下降子序列(或者求其最长不下降子序列)的长度。

以最长下降子序列为例

用a[i]存储序列a的第i个元素(i: 1 to n)

用f[i]表示算上第i个位置的元素时最长子序列为f[i],

O(n^2)解法:

就是说在1 --- i -1之间必可以找到下标为j的元素a[j]使得f[j]是f[1]---f[i-1]之中最大的,则f[i] = f[j] + 1.

(注意要满足a[j]>a[i])

当i (1 to n)求得f[1] -- f[n]后只要再求得f[1]--f[n]之中最大的就是ans了。

状态转移方程:

f[i] = 1 (i = 1)//只有第一个字符

f[i] = f[j] + 1(a[i] < a[j] )//若是最长不下降则满足a[i] >= a[j].

代码实现:

//h[i]为母序列,dp[i]代表到第i个位置算上h[]i后得到的最长下降子序列长度ans是最长下降子序列长度
//f[i]代表到第i个位置算上h[]i后得到的最长不下降子序列长度min?是最长不下降子序列长度
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;

#define maxint 0x7f7f7f7f
const int N = 100000;

int h[N],dp[N],f[N];
int main(){
   // freopen("in.txt","r",stdin);
   // freopen(".txt","w",stdout);
    int n = 1;
int ans = -maxint,min = -maxint;
while(scanf("%d",&h[n]) != EOF){
   n ++;
}
dp[1] = f[1] = 1;
for(int i = 1; i < n; i ++){
    dp[i] = 1;
    f[i] = 1;
    for(int j = 1; j < i; j ++){
        if(dp[j]+1>dp[i] && h[j] > h[i])
            dp[i] = dp[j]+1;
        if(f[j]+1>f[i] && h[j] < h[i])
            f[i] = f[j]+1;
    }
}
for(int i = 1; i < n; i ++){
    if(ans<dp[i]){
        ans = dp[i];
    }
    if(min<f[i])
        min = f[i];
}

printf("%d\n%d\n",ans,min);

return 0;
}

O(n*logn)解法

思路:

令数组c[k]记录使f[]= k时的a[i]的最小值,len表示此时最长下降子列的长度

在第i个位置有两种情况

1.a[i]<c[k],此时满足降序只需将a[i]接在c[k]后面,len +1;

2.a[i]>=c[k],则需要在a[1] 到c[k]之间找到一个大于它的最小值a[j],然后将a[i]置于j+1的位置,len = k = j+1.

由于c[k]不然具有单调性因而寻找a[j]的过程可以用二分。这也就是算法复杂度达到O(n*logn)的原因。

最后len的值也就是最长子序列的长度。

代码实现:

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1000000;
//二分查找返回大于a[i]的最小值的下标
int bSearch(int c[],int len,int n){
int left = 1,right = len,mid;
while(left <= right){
      mid = (left+right)/2;
  if(n > c[mid])
     right = mid - 1;
  else if(n < c[mid])
     left = mid + 1;
  else
  return mid;

}
return left;
}

int a[N],c[N];
int main(){
     int n = 1,j,len;
     int count = 0;
 // freopen("in.txt","r",stdin);
   // freopen("out.txt","w",stdout);
while(scanf("%d",&a[n]) != EOF){
   n ++;
}
     c[1] = a[1];
      len = 1;
      for(int i = 1; i < n; i ++){
          j = bSearch(c,len,a[i]);
        c[j] = a[i];
        if(j > len)//没找到,说明a[i]<c[k],根据二分查找的特点刚好j比len大一,将a[i]加到c[len+1]的位置
          len = j;//更新len
      }
      printf("  ans = %d\n",len);

return 0;
}

最长下降子序列O(n^2)及O(n*log(n))解法

时间: 2024-10-08 11:12:42

最长下降子序列O(n^2)及O(n*log(n))解法的相关文章

BUY LOW, BUY LOWER_最长下降子序列

Description The advice to "buy low" is half the formula to success in the bovine stock market.To be considered a great investor you must also follow this problems' advice: "Buy low; buy lower" Each time you buy a stock, you must purcha

HDU - 6197 array array array (最长上升子序列&amp;最长下降子序列)

题意:对于一个序列,要求去掉正好K个数字,若能使其成为不上升子序列或不下降子序列,则“A is a magic array.”,否则"A is not a magic array.\n". 分析: 1.求一遍LCS,然后在将序列逆转,求一遍LCS,分别可得最长上升子序列和最长下降子序列的长度tmp1.tmp2. 2.n - tmp1 <= k或n - tmp2 <= k即可,需要去掉的去完之后,在已经是最长上升或最长下降的序列中随便去够k个就好了. #include<

POJ 1887 Testingthe CATCHER (LIS:最长下降子序列)

POJ 1887Testingthe CATCHER (LIS:最长下降子序列) http://poj.org/problem?id=3903 题意: 给你一个长度为n (n<=200000) 的数字序列, 要你求该序列中的最长(严格)下降子序列的长度. 分析:        读取全部输入, 将原始数组逆向, 然后求最长严格上升子序列就可以. 因为n的规模达到20W, 所以仅仅能用O(nlogn)的算法求.        令g[i]==x表示当前遍历到的长度为i的全部最长上升子序列中的最小序列末

导弹拦截(最长下降子序列)变形

题目描述 Description 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度.某天,雷达捕捉到敌国的导弹来袭.由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹. 输入描述 Input Description 输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数) 输出描述 Output Description 输出这套系统最多能拦截多少导弹

POJ 1836 Alignment(DP max(最长上升子序列 + 最长下降子序列))

Alignment Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 14486   Accepted: 4695 Description In the army, a platoon is composed by n soldiers. During the morning inspection, the soldiers are aligned in a straight line in front of the cap

(hdu step 3.2.4)FatMouse&#39;s Speed(在第一关键字升序的情况下,根据第二关键字来求最长下降子序列)

在写题解之前给自己打一下广告哈~..抱歉了,希望大家多多支持我在CSDN的视频课程,地址如下: http://edu.csdn.net/course/detail/209 题目: FatMouse's Speed Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 1034 Accepted Submission(s): 526   Proble

低价购买 (动态规划,变种最长下降子序列(LIS))

题目描述 “低价购买”这条建议是在奶牛股票市场取得成功的一半规则.要想被认为是伟大的投资者,你必须遵循以下的问题建议:“低价购买:再低价购买”.每次你购买一支股票,你必须用低于你上次购买它的价格购买它.买的次数越多越好!你的目标是在遵循以上建议的前提下,求你最多能购买股票的次数.你将被给出一段时间内一支股票每天的出售价(216范围内的正整数),你可以选择在哪些天购买这支股票.每次购买都必须遵循“低价购买:再低价购买”的原则.写一个程序计算最大购买次数. 这里是某支股票的价格清单: 日期  1 

HDOJ(1069)最长下降子序列

每个箱子可有3种叠加方式,所以有3*n个箱子.将箱子按长度由大到小排序,有求箱子按宽度的最长下降子序列的高度之和即可. #include<cstdio> #include<algorithm> #include<cstring> using namespace std; #define MAX(a,b) (a>b)?a:b #define MIN(a,b) (a>b)?b:a const int SIZE=100+6;//尽量大一些; struct node

【最长下降子序列的长度和个数】 poj 1952

转自http://blog.csdn.net/zhang360896270/article/details/6701589 这题要求最长下降子序列的长度和个数,我们可以增加数组maxlen[size](记录当前第1个点到第i个点之间的最长下降序列长度)和maxnum[size](记录1~i之间的最长下降序列个数 ),首先对于最长下降序列属于DP基础题,只要对每一个a[i]求出符合要求(a[i] < a[j])的max( maxlen[j] + 1)即可,主要难点在第二步求下降序列总数 在序列中,