贪心+二分 O(nlogn)求LIS(最长上升子序列)并记录路径

如果用dp求LIS的话,记录路径是比较简单的,只要在更新dp数组的时候同时记录路径,最后找LIS长度的时候同时找到终点,通过终点逆向就能找到LIS路径了(这是我临时想的,可能有更好的方法,不保证正确性

for (int i = 1; i <= n; i++)
    for (int j = 1; j < i; j++)
        if (a[j] < a[i] && f[i] < f[j] + 1){
            f[i] = f[j] + 1;
            path[i] = j;
        }
int s;
for (int i = 1; i <= n; i++)
    if(ans<f[i]){
        ans=f[i];
        s=i;
    }
while(s!=0){
    printf("%d ",a[s]);//这是逆向输出路径
    s=path[s];
}

但是用贪心+二分来求LIS时,怎么找LIS路径呢? 注意,low数组中存的并不一定是正确LIS,但是不影响求LIS长度。

其实这个算法记录路径也并不复杂,我们在遍历目标数组时,数组里的每一个数都是有机会进入low数组的,所以我们构造一个pos数组,在每个数进入low时,记录该数在low的位置,表示以该数结尾的LIS长度,原数组遍历结束后,从右往左寻找pos中的最大值,最终就会得到一个倒着的LIS序列,代码也比较好理解

low[1] = a[1];
int len = 1;
pos[1] = len;
for (int i = 2; i <= n; i++)
{
    if (a[i] >= low[len])//严格上升的话不能等
    {
        low[++len] = a[i];
        pos[i] = len;
    }
    else
    {
        int index = lower_bound(low + 1, low + 1 + len, a[i]) - low;
        low[index] = a[i];
        pos[i] = index;
    }
}
int maxx = INF, tem = len;
for (int i = n; i >= 1; i--)
{
    if (tem == 0)
        break;
    if (pos[i] == tem && maxx >= a[i])//严格上升的话是'>'
    {
        tem--;
        maxx = a[i];
    }
}
原文地址:https://www.cnblogs.com/Zeronera/p/11317835.html