动态规划专题小结:最长上升子序列(LIS)问题

(1)问题描述:给定n个整数A1,A2,A3...An。按照从左往右的顺序选择尽可能多的整数,组成一个上升子序列,其中相邻元素不能相等。

(2)解题思路:本题就是经典的最长上升子序列问题(Longest Increasing Subsequence,LIS)。可以通过动态规划解决。定义状态d(i)表示以下标i结尾的LIS的最大长度。那么不难得到如下状态转移方程:

d(i)=max{0,d(j)|j<i,Aj<Ai}+1;

最终的答案为max{d(i)}。时间复杂度为O(N^2)。由于这种方法十分常见,不予赘述。接下来介绍O(N*logN)的算法。


考虑这样一个事实,给定两个下标a,b(注意,这里a,b大小未知),如果它们满足Aa<Ab且d(a)==d(b)。则对于所有的i>max{a,b},a并不会比b差。另外,如果b满足Ab<Ai,那么对于a也满足这个性质,且二者的d值相同。但是,如果Aa<Ai,却不一定有Ab<Ai。

通过以上的事实,我们发现,对于某一个d’值,只要保留最小的那个Aa,使得d(a)==d‘即可。我们用g(i)表示d值为i的最小状态的编号(即i对应的最小的那个Aa,如果不存在设置为INF)。根据以上的推理有如下不等式:

g(1)≤g(2)≤g(3)≤...≤g(n)

注意,上述的g(i)是会动态改变的。对于一个给定的状态i,我们只考虑在i之前已经计算过的状态j(即j<i),上述的g值也是基于这些状态而改变的。随着i的增大,我们要考虑的状态也越来越多,g也随之发生改变。在给定状态i时,可以用二分查找得到满足g(k)≥Ai的第一个下标k(实际上是要找g(k')<Ai的最后一个下标k',则d(i)=k'+1,令k=k'+1即可得到,这里用lower_bound省略了k'+1的操作),则d(i)=k,此时Ai<g(k).而d(i)=k,所以更新g(k)=Ai(此时正是利用了上述的事实!虽然这里的g(k)在变小,但仍然大于等于g(k-1),满足上述的不等式)。

typedef long long ll;
typedef unsigned long long ull;
#define me(s) memset(s,0,sizeof(s))
#define For(i,n) for(int i=0;i<(n);i++)

#define N 100
#define INF 100000000
int a[N];
int g[N];
int d[N];

int main()
{
    freopen("t.txt","r",stdin);
    int n;
    while(~scanf("%d",&n))
    {
        me(a);me(g);me(d);
        fill(g+1,g+n+1,INF);
        For(i,n)
        scanf("%d",&a[i]);
        For(i,n)
        {
            int k=lower_bound(g+1,g+n+1,a[i])-g;
            d[i]=k;
            g[k]=a[i];
        }
        printf("%d\n",d[n-1]);
    }
    return 0;
}




你可能感兴趣的:(动态规划,LIS)