最长递增子序列(LIS)的两种实现

最长递增子序列(LIS)

1.最长递增子序列的问题描述
给定一序列L=(a1,a2,a3,a4......an-1,an),存在序列L'=(ai1,ai2,ai3......aik)满足ai1<ai2<ai3......<aik,且i1<i2<i3<......<ik。则称L'是序列L的一个递增子序列,显然最长递增子序列就是,所有满足这个条件的序列里面长度最大的。
2.有两种算法解决
方法(1).用一个序列L1保存L排序后的结果,然后找L1和L的最长公共子序列。(在这里不涉及,主要讲方法二的两个实现)
方法(2).动态规划实现。由于简单递推公式直接给出。opt[i]表示以ai为结束时的最长子序列的长度。
所以opt[0] = 1,当i > 0时:opt[i] = max {opt[j]+1,1}(j<i,且a[j] < a[i])。
int LIS1 (int data[], int n)
{
	if (n == 0)
		return 0;
	int ans = opt[0] = 1;
	for (int i = 1; i < n; ++i)
	{
		opt[i] = 1;
		for (int j = 0; j < i; ++j)
		{
			if (data[i] > data[j])
				opt[i] = max(opt[j] + 1, opt[i]);
		}
		ans = max (opt[i], ans);
	}
	return ans;
}
以上实现方式算法复杂度为O(n^2).其中在寻找opt[j]时花了O(n)的算法复杂度,可以改用二分查找将算法浮渣度降到O(logn),总的算法复杂度为O(nlogn).其中用到了一个辅助数组,用于保存 最大递增子序列的最末元素。实现如下:
int LIS2 (int data[], int len)
{
	if (len == 0)
		return 0;

	for (int i = 0; i < len; ++i)
		assit[i] = MAXNUM;

	assit[0] = data[0];
	opt[0] = 1;
	int ans = 1;

	int low, high, mid;
	for (int  i = 1; i < len; ++i)
	{
		low = 0;
		high = i;
		while (low < high)
		{
			mid = low + ((high - low)>>1);
			if (assit[mid] < data[i])
				low = mid + 1;
			else
				high = mid;
		}

		assit[low] = data[i];
		opt[i] = low + 1;
		ans = max (opt[i], ans);
	}
	return ans;
}

POJ2533可以验证此算法的正确性 
AC代码如下:
#include <cstdio>
#include <algorithm>

using namespace std;

const int MAXN = 1002;
const int MAXNUM = 10009;
int data[MAXN];
int assit[MAXN];   //最大递增子序列的最末元素
int opt[MAXN];
int n;

int LIS1 (int data[], int n)
{
	if (n == 0)
		return 0;
	int ans = opt[0] = 1;
	for (int i = 1; i < n; ++i)
	{
		opt[i] = 1;
		for (int j = 0; j < i; ++j)
		{
			if (data[i] > data[j])
				opt[i] = max(opt[j] + 1, opt[i]);
		}
		ans = max (opt[i], ans);
	}
	return ans;
}

int LIS2 (int data[], int len)
{
	if (len == 0)
		return 0;

	for (int i = 0; i < len; ++i)
		assit[i] = MAXNUM;

	assit[0] = data[0];
	opt[0] = 1;
	int ans = 1;

	int low, high, mid;
	for (int  i = 1; i < len; ++i)
	{
		low = 0;
		high = i;
		while (low < high)
		{
			mid = low + ((high - low)>>1);
			if (assit[mid] < data[i])
				low = mid + 1;
			else
				high = mid;
		}

		assit[low] = data[i];
		opt[i] = low + 1;
		ans = max (opt[i], ans);
	}
	return ans;
}

int main ()
{
	scanf ("%d", &n);

	for (int i = 0; i < n; ++i)
		scanf ("%d", &data[i]);

	printf ("%d", LIS2(data, n));
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值