貪心+二分 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]; } }
相關文章
相關標籤/搜索