LIS 求最长上升子序列并输出字典序最小的解

LIS 求最长上升子序列并输出字典序最小的解

    • las[i] 记录在求LIS 过程中出现在 DP[i] 位置的最后一个原序列中 a[x] 的数组下标x. fir[ ]同理。

// An highlighted block
#include <cstdio>
#include <algorithm>
#include <stack>
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <cstring> 
#define inf 2147483645
using namespace std;
int n,  a[100005], dp[100005], poss[100005], len, last, fir[100005], las[100005];
stack <int> s;
int main()
{
     cin>>n;
     for (int i=1; i<=n; i++) 
         cin>>a[i];
    for (int i=1; i<=n; i++) dp[i] = inf;
    dp[1] = a[1], len = 1; poss[1] = 1; last = 1; fir[1] = las[1] = 1;
	for (int i=2; i<=n; i++) {
    	if (a[i] > dp[len]) {		
		    poss[i] = ++len;
		    fir[len] = las[len] = i;
    		dp[len] = a[i];
    		last = i;
    	} else { 
    		poss[i] = lower_bound(dp+1, dp+1+len, a[i]) - dp;
    		dp[poss[i]] = a[i];
    		las[poss[i]] = i;
    	}
    }
    cout<<len<<endl;
    
    for (int i=len-1; i>=1; i--){
    	for (int j=las[i+1]; j>=1; j--){
    		if (poss[j] == i) {
    			las[i] = j;
    			break;
    		}
    	}
    }
    int p = fir[1]; int minn;
    for (int i=1; i<=len; i++){
    	minn = 2147483640;
    	for (int j=p; j<=las[i]; j++){
    		if (poss[j] == i && a[j] < minn){
    			p = j;
    			minn = a[j];
    		}
    	}
    	cout<<minn<<" ";
    }
/*    s.push(a[last]);
	for (int i=len-1; i>=1; i--){
    	for (int j=last-1; i; i--){
    		if (poss[j] == i) {
    			last = j;
    			s.push(a[last]);
    			break;
    		}
    	}
    }
    while (!s.empty()){
    	cout<
    return 0;
}

las[i] 记录在求LIS 过程中出现在 DP[i] 位置的最后一个原序列中 a[x] 的数组下标x. fir[ ]同理。

  • S1: 从长到短筛选合法的 las[i] ( 1…i…len-1 ) , 即在 las[i] 位置之后一定存在一系列 比 poss[i] 大的数。
    例如 : 原序列 a[] : 2 6 8 2 5 9 4 7
    poss[]: 1 2 3 1 2 4 2 3
    lis_len = 4
    fir[]: 1 2 3 6
    初始las[]: 4 7 8 6
    显然 las[3] = 8 时 后面没有 poss[i] = 4, 所以 a[8] 是非法的。
    修改后的 las[] : 1 2 3 6
    一定要从大到小修改)) : 这样可以保证从 fir[i] 到 las[i] 的闭区间里的任意一个 poss[] 为 i 的元素开头都存在一个合法的最长上升子序列。

  • S2: 从 fir[1] 到 las[1] 中找到 poss[] 为 1 的 字典序最小的开头, 再以此类推。

你可能感兴趣的:(算法题)