动态规划总结

问题1:最长上升子序列问题(LIS)

分析:设d(i)为以i为结尾的最长上升子序列的长度,则d(i)=max{ 1 , d(j)+1} ( j的值为1,2,3,...,i-1且Aj<Ai ),最终答案是max{d(i)}。如果LIS中的相邻元素可以相等,把   小于号改成等于号即可。算法的时间复杂度为O(n^2)

        一种时间复杂度是O(nlogn)的方法。假设已经计算出的两个状态a和b满足Aa<Ab且d(a)=d(b),则对于后续所有状态i(即i>a且i>b)来说,a并不会比b差——如果b满足 Ab<Ai的条件,a也满足,且二者的d值相同;但反过来却不一定了,a满足Aa<Ai的条件时,b却不一定满足。换句话说,如果我们只保留a,一定不会丢失最优解。

        这样对于相同的d[]值,只需保留A最小的一个。对于d[i]=k,我们用g[k]表示满足d[]值等于k的所有A[i]中的最小值。即g[k]=min{A(i)}(d[i]=k)。

        设当前已经求出的最长上升子序列长度为len,先判断A[i]与g[len],若A[i]>g[len],把A[i]接在g[len]之后得到一个更长的上升子序列,len=len+1,g[len]=A[i];否则, 在g[1]...g[len]中找到最大的j,满足g[j]<A[i],令k=j+1,将A[i]接在g[j]之后得到一个更长的上升子序列,更新g[k]=A[i]。最后,len即为所要求的最长上升子序列。

        在上述算法中,如果用朴素的顺序查找g[1]...g[len],由于有O(n)个元素要查找,每次查找的时间复杂度是O(n),所以整体复杂度是O(n^2)。但由于g[len]有如下特点:g[1]<g[2]<...<g[len],可以用二分查找,整个算法时间复杂度降为O(nlogn)

        for(int i=1;i<=n;i++) g[i]=INF;

        for(int i=0;i<n;i++){

        int k=lower_bound(g+1,g+1+n,A[i]) - g;

        d[i]=k;

        g[k]=A[i];

        }

函数lower_bound()在first和last中的前闭后开区间进行二分查找,返回大于或等于val的第一个元素位置。如果所有元素都小于val,则返回last的位置

举例如下:
一个数组number序列为:4,10,11,30,69,70,96,100.设要插入数字3,9,111.pos为要插入的位置的下标

pos = lower_bound( number, number + 8, 3) - number,pos = 0.即number数组的下标为0的位置。
pos = lower_bound( number, number + 8, 9) - number, pos = 1,即number数组的下标为1的位置(即10所在的位置)。
pos = lower_bound( number, number + 8, 111) - number, pos = 8,即number数组的下标为8的位置(但下标上限为7,所以返回最后一个元素的下一个元素)。
所以,要记住:函数lower_bound()在first和last中的前闭后开区间进行二分查找,返回大于或等于val的第一个元素位置。如果所有元素都小于val,则返回last的位置,且last的位置是越界的!!~
返回查找元素的第一个可安插位置,也就是“元素值>=查找值”的第一个元素的位置

     

原文地址:https://www.cnblogs.com/sage-blog/p/3648291.html