贪心+二分 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序列,代码也比较好理解spa

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]; } }
相关文章
相关标签/搜索