• LIS最长上升子序列问题+记录路径


    (O(nlogn))LIS

    (nlogn)做法

    维护(dp)数组使得每次加入元素,如果是大于(dp)数组尾部,那么直接加到最后面,如果不是,那么加入到对答案影响最好的位置,就是严格大于的下一个位置,插入时用二分查找可降低至(log)

    如果要记录路径,那么就可以每次从(a)数组里加入到(dp)中时,可以记录下(a[i])的位置,然后从(n)倒着遍历(a)数组,将最先遇到的符合答案位置的(a[i])元素加入到答案数组中。

    证明

    我觉得还是证明一下这样做的正确性。

    首先得知(dp)数组在(O(nlogn))的做法当中所储存的并不是最长上升子序列,而是对答案最优的状态。

    (dp)数组里虽然是上升序列,但是它却是会由后部分的数插入到(dp)从而使(dp)更有利于答案,但是同时改变了数的位置,导致后插的数到了前面。

    首先确定的是,(dp)数组里的数一定是递增的并且是最长的,并且一定全部最长公共子序列中的全部元素一定曾经在里面呆过,我们的(dp)数组唯一就差在把元素位置弄乱了,那么我们每次将(a)元素插入的时候,一定有对应的位置对于(dp)数组,然后我们就记录(a)对应(dp)的位置,然后从后往前,先遇到的先匹配,一定避免了所得答案不是(a)数组原顺序的情况,然后就会得到某个最长公共子序列。

    记录路径的例题HDU 1160

    #include <iostream>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    const int N = 1009;
    int dp[N];
    struct node {
        int w, s, idx;
        bool operator<(node a) const {
            if (a.s == s )return a.w < s;
            return a.s < s;
        }
    }a[N];
    int main() {
        int n = 1;
        while (cin >> a[n].w >> a[n].s) a[n].idx = n,n++;
        n--;
        sort(a + 1, a + 1 + n);
        int ans[1100]{};
        int pos[1100]{};
        int len = 1;dp[len] = a[1].w;
        ans[len] = 1;
        for (int i = 1; i <= n; i++) {
            if (a[i].w > dp[len]) {
                dp[++len] = a[i].w;
                pos[i] = len;
            }  else {
                int p = lower_bound(dp + 1, dp + 1 + len, a[i].w) - dp;
                dp[p] = a[i].w;
                pos[i] = p;
            }
        }
        int nn = len;
        for (int i = n; i >= 1; i--) {
            if (pos[i] == len) {
                ans[len] = a[i].idx;
                len--;
            }
        }
        cout << nn << endl;
        for (int i =1; i <= nn; i++) {
            cout << ans[i] << endl;
        }
    }
    
    
    
  • 相关阅读:
    java
    java
    java
    java
    java
    java
    java
    java
    sed命令的用法
    linux系统产生随机数的6种方法
  • 原文地址:https://www.cnblogs.com/Xiao-yan/p/14107825.html
Copyright © 2020-2023  润新知