最长上升子序列与最长下降子序列logn+最优解打印

emmm...还是看的题解,还不是很懂,但是代码还挺简单的,先记下来

2021/3/14更:

今天实验室大佬讲了一下最长递增子序列logn求法同时求出最优解(如果存在多个长度相同,则字典序最小),听说是去年蓝桥杯国赛的题,

先说明一下变量,val数组存的基础变量,pre数组是logn求法必要的那个数组,len是最长递增子序列长度,pos表示在pre数组中的每个数在val数组中的位置

大佬题解的fa表示该点如果存在结果序列中,则fa标记的点也必定在结果序列中,我的fa表示pre数组中某个数的上一个数在val数组中的位置,结果数组res

可能有点绕,结合描述和代码看看吧

大佬给出的题解是:

在二分查找出要插入位置p之后,p之前位置的数的相对位置一定是比当前位置要小同时数值也更小,假如后一个数出了,前一个数也必定要出,用fa[]标记起来

如果前面的数被替换掉,替换的那个数的相对位置一定是比当前数要大的,所以与当前数是无关的,,然后标记fa[0]=-1为终止点,就能从fa[len]到fa[0]打印出结果路径了

我的思路:

我原本是听大佬讲完了这道题,以为自己写的就是大佬跟咱们说的,所以才会有fa但是意义不一样=-=,但是这都不重要了...

求出pre数组后,我们可以知道的是,pre数组的最后一位一定是在最终解res数组中的最后一位的,自己可以想象理解一哈,

然后我们就可以从后往前选,如果相对位置大于res数组后一个位置的数在val数组中的相对位置,则找上一个放在这里的数,看相对位置得行不,得行就取他

而我们替换的原则又是小的替换大的(在最长递增子序列中),所以只要取到一个相对位置满足条件的,它的值一定是比这个位置它上一次替换掉的数小的,

所以字典序就是最小的了,这样咱们就能得到一个res数组啦,比大佬的程序稍微慢一点,但量级是相同的...

由于没有测试题可以实验,可能会有错,欢迎指出

大佬的简介代码:

#include <cstdio>
#include <iostream>
#include <string>
#include <cstring>
#include <algorithm>
#include <map>
#include <queue>
using namespace std;
const int maxn=1e5+100;
int a[maxn];
int len,n;
int tmp[maxn];
int fa[maxn],pos[maxn];
void Print(int x){
    if(x==-1)return;
    Print(fa[x]);
    printf("%d ",a[x]);
}
int main(){
    scanf("%d",&n);
    pos[0]=-1;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        int p=lower_bound(tmp+1,tmp+1+len,a[i])-tmp;//插入位置
        if(len+1==p)len++;
        tmp[p]=a[i];
        pos[p]=i;//记录插入位置当前下标
        fa[i]=pos[p-1];//记录刚插入前一位的下标
    }
    Print(pos[len]);
    return 0;
} 
/*
8
65 158 170 299 300 155 207 389

*/

我的代码:

#include<iostream>
#include<string.h>
#include<cmath>
#include<set>
#include<map>
#include<string>
#include<queue>
#include<stack>
#include<vector>
#include<bitset>
#include<algorithm>
using namespace std;
typedef long long ll;
inline int read() {
    int sum = 0, f = 1;
    char p = getchar();
    for (; !isdigit(p); p = getchar()) if (p == '-')f = -1;
    for (; isdigit(p); p = getchar())  sum = sum * 10 + p - 48;
    return sum * f;
}
const int maxn = 100005;
int val[maxn];
int pre[maxn];
int fa[maxn];
int pos[maxn];
int len,n;
int main() {
    freopen("test.txt", "r", stdin);
    n = read();
    for (int i = 1; i <= n; i++) {
        val[i] = read();
    }
    len = 0;
    pre[0] =0;
    for (int i = 1; i <= n; i++) {
        if (pre[len] < val[i]) {
            pre[++len] = val[i];
            pos[len] = i;
        }
        else {
            int p = lower_bound(pre + 1, pre + len + 1,val[i]) - pre;
            fa[i] = pos[p];
            pos[p] = i;
            pre[p] = val[i];
        }
    }
    cout << len << endl;
    vector<int>res(len + 1, 0);
    int lastpos = pos[len];//pre数组中的最后一个数一定res数组的最后一个,找到最后一个数的位置
    //然后从后往前找
    for (int i = len - 1; i >= 1; i--) {
        int now = pos[i];
        while (now > lastpos) {//如果该位置的相对位置比后一个的相对位置大,继续找,直到找到一个小于的
            now = fa[now];
        }
        res[i] = val[now];
        lastpos = now;
    }
    res[len] = val[pos[len]];
    for (int i = 1; i <= len; i++) {
        cout << res[i] << " ";
    }
    return 0;
}

 

posted @ 2021-01-30 23:35  cono奇犽哒  阅读(178)  评论(0)    收藏  举报