最长递增子序列(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;
}