zoukankan      html  css  js  c++  java
  • 贪心+二分 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];
        }
    }
  • 相关阅读:
    工具
    选择排序
    c#中queue的用法
    c#加密
    话谈c#拷贝
    const与readonly的区别
    WinForm中使MessageBox实现可以自动关闭功能
    c#winform关闭窗口时触发的事件
    委托
    C# STA和MTA线程设置
  • 原文地址:https://www.cnblogs.com/Zeronera/p/11317835.html
Copyright © 2011-2022 走看看