(2)最长不下降子序列问题____动态规划

最长不下降子序列问题即是求:一数列中某一严格单增的子序列的最长长度.

举例:6253174中最长不下降的子序列257237234.即是最长长度为3.

一、简单的O(N^2)算法

当我们定义问题F(i)为以bi结束的最长不下降序列时,则问题F(I)
有I-1个子问题:F(1), F(2),…, F(I-1)。我们要使F(I)最大,则要找
到一个F(j)(1≤j≤I-1)最大的子问题,且同时满足Bj < Bi,这时F(I)=F(j)max+1;

如果不存在Bj < Bi(1≤j≤I-1),那么i不存在前驱节点,那么F(I)=1;

//用两个数组a[]数组存数num[]数组存这个数对应的最长不下降子序列长度

int longest(int a[],int n)
{
    int num[n];
    int i,j;
    for(i=0;i<n;i++) //每次循环就确定了前i项中的最长不下降子序列的长度并存放在num[i]中.
    {
        num[i]=1;
        for(j=0;j<i;j++)
        {
            if(a[j]<a[i]&&num[j]+1>num[i])  找出当前的前去节点 并更新长度.
            num[i]=num[j]+1;
        }
    }
    int max=0;
    for(i=0;i<n;i++)
    if(max<num[i])
    max=num[i];
    return max;
}


二、复杂的O(NlogN)算法

概述:O(NlogN)的算法在于它建立了一个数组(这里假设为f []).那么f[i]表示读到当前数据的时候  (这个f[]是每读入题目数组的一个元素的时候就更新一次)  长度为i的严格单增序列中结尾元素的最小值,用t来表示当前f[]数组的长度,那么当题目数组全部读完后,t的值就是最长不下降子序列.

具体点来讲:

                   设当前的已求出来的长度为t,则判断a[i]和b[k];

                  1.如果a[i]>b[t].即是a[i]大于长度为 t 的序列中的最后一个元素,这样加入a[i]在这个元素可以使当前序列长度加1.t=t+1,然后现在的b[k]=a[i].

                  2.如果a[i]=b[i],即是a[i]]等于长度为 t 的序列中的最后一个元素,那么这个元素当前有无都不影响.

                  3.如果a[i]<b[k],那么在b[1]……b[k]中找到最大的 j 使得b[j]<a[i],所以a[i]加入长度为j的j序列里面,这个序列的长度加1,那么就可以更新长度为j+1的序列的最后一个元素,即是b[j+1]=a[i].

#include <stdio.h>
int f[40010];
int longest(int a,int l,int r)   //定义二分查找函数
{
	int m;
	while(l<=r)
	{
		m=(l+r)/2;
		if(f[m]==a)   //如果出现当前值和查找的值相同那么自然就更新当前的f[]值,也可理解为不用更新
		{
			l=m;
			return l;
		}
		else if(f[m]>a)
			r=m-1;
		else
			l=m+1;
	}
	return l;   //每次返回的都是在f[]中最大的j能够使得f[j]<a[i]的j+1.
}
int main()
{
	int N,n,a,i,t,k,j;
	scanf("%d",&N);
	while(N--)
	{
		t=0;
		scanf("%d",&n);
		for(i=0;i<n;i++)
		{
			scanf("%d",&a);  //每次输入一个数就处理一个数
			k=longest(a,1,t);
			if(k<=t)          //如果k<=t说明是情况3.
				f[k]=a;
			else              //否则为情况1,表示最大长度可以更新
			{t++;f[t]=a;}
		}
		printf("%d\n",t);
	}
	return 0;
}


                   算法复杂度的分析:

                方法一: 因为共有n个元素要进行计算,每次计算又要查找i次(i从1递增到n),那么时间复杂度就是O(N^2).

                方法二:由于f[]数组里面的数是单调递增的,那么查找的时候可以不用遍历而用二分查找,这样时间复杂度就为O(NlogN).




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值