九度OJ 1533 最长上升子序列 (基于贪心和二分查找)

题目描述:

给定一个整型数组, 求这个数组的最长严格递增子序列的长度。 譬如序列1 2 2 4 3 的最长严格递增子序列为1,2,4或1,2,3.他们的长度为3。

输入:

输入可能包含多个测试案例。
对于每个测试案例,输入的第一行为一个整数n(1<=n<=100000):代表将要输入的序列长度
输入的第二行包括n个整数,代表这个数组中的数字。整数均在int范围内。

输出:

对于每个测试案例,输出其最长严格递增子序列长度。

样例输入:
4
4 2 1 3
5
1 1 1 1 1
样例输出:
2
1


【思路分析】

   求LIS的经典问题,用到的不是DP里面O(n*n)的方法,而是用了基于贪心、二分查找的O(nlogn)方法(为什么没有命名成XXX算法 = =)

   设a[n]为原序列,d[n]为长度为n的上升子序列的最后一个元素,当有多个长度为n的上升子序列时,取这些子序列中末尾最小的元素作为d[n]的值。为什么选最小的呢?因为最小的最有“潜力”(贪心)。举个例子,假设原序列为:2,1,8,3,7,5,6,对于d[3],长度为3的上升子序列有1,3,7和1,3,5两个,那么d[3]取值为5,即末尾最小的元素。因为在这个子序列后可能存在x满足 5 < x < 7(即x == 6),因此要是子序列确定为1,3,7的话,则显然不如1,3,5,6长。可见,d数组中的元素是单调递增的。

   有了上述的贪心策略,便可以进行下面的操作了。首先,令len = 1,d[1] = a[1],当a[i] > d[len]时,有d[++len] = a[i],即加入新的元素来扩充上升子序列;否则,从d[1]到d[len - 1]找到一个j,使得a[i]满足:  d[j - 1] < a[i] < d[j],这时有d[j] = a[i],即用a[i]来替换d[j](也就是a[i]比d[j]更有“潜力”)。又由于d数组是单调递增的,因此可以用二分查找O(logn)很快找到j的值。

   最后用a = {2,1,5,3,6,4,8,9}这个序列来过一遍上述的流程。首先,len = 1,d[1] = a[1] = 2。则a[i],len,d[len]的值的变化见下表:

   九度OJ 1533 最长上升子序列 (基于贪心和二分查找)_第1张图片

代码如下:
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int maxn = 100005;
int n;
int a[maxn];
int d[maxn];//记录长度为i的上升子序列最后一个元素的值
int binSearch(int key,int left,int right)
{
    while(left <= right)
    {
        int mid = (left + right) >> 1;
        if(key > d[mid] && key <= d[mid + 1])//贪心策略
        {
            return mid;
        }
        else if(key > d[mid])
        {
            left = mid + 1;
        }
        else
        {
            right = mid - 1;
        }
    }
    return 0;
}
int LIS(int n)
{
    d[1] = a[1];
    int len = 1;
    int j = 0;
    for(int i = 2;i <= n;i++)
    {
        if(d[len] < a[i])
        {
            j = ++len;//直接向后插入
        }
        else
        {
            j = binSearch(a[i],1,len) + 1;//找到替换位置
        }
        d[j] = a[i];
    }
    return len;
}
void init()
{
    for(int i = 1;i <= n;i++)
    {
        scanf("%d",&a[i]);
    }
}
void solve()
{
    printf("%d\n",LIS(n));
}
int main()
{
    while(scanf("%d",&n) != EOF)
    {
        init();
        solve();
    }
    return 0;
}


  

  

你可能感兴趣的:(ACM__贪心,ACM__基础算法)