最长上升子序列与最长下降子序列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; }