熱身訓練2 The All-purpose Zero
阿新 • • 發佈:2021-07-21
簡要題意:
長度為n的陣列,每個數字為S[i],$0$是一種很神奇的數字,你想要的,它都可以變!
問這個序列的最長上升子序列長度為多少?
分析:
我們將除了‘0’以外的S[i],減去i之前出現的‘0’的個數,最後求得排除‘0’後的最長上升子序列長度,加上‘0’的個數,就是我們要求的答案。
在這裡我不主要分析該做法的正確性,我們引入一個O(nlogn)的方法來求最長上升子序列。
LIS(點此看題)
貪心+二分
我們令f[i]表示長度為i的上升子序列,末尾的最小值。
這裡我們因為貪心,所以子序列末尾越小越好。
我們按順序列舉給出的陣列S[i],然後對f[]二分查詢除小於S[i]的最大的f[j],並用它將f[j+1]更新。
#include<bits/stdc++.h> using namespace std; #define re register int #define LL long long const int N=1e5+5; int n, a[N], b[N], len; signed main() { scanf("%d",&n); for(re i=1;i<=n;++i) scanf("%d",&a[i]); for(re i=1;i<=n;++i) { if(a[i] > b[len]) b[++len] = a[i];else { int tp = lower_bound(b+1, b+1+len, a[i])-b; b[tp] = a[i]; } } printf("%d", len); }
上馬
#include<bits/stdc++.h> using namespace std; #define re register int #define LL long long const int N=1e5+5; int n, a[N], b[N], num, len; inline void work() { numView Code= len = 0; scanf("%d",&n); for(re i=1;i<=n;++i) { scanf("%d",&a[i]); if(a[i] == 0) { num ++; a[i] = 1e9; } else { a[i] -= num; } } b[0] = -1e9; for(re i=1;i<=n;++i) { if(a[i] == 1e9) continue; if(a[i] > b[len]) b[++len] = a[i]; else { int tp = lower_bound(b+1, b+1+len, a[i])-b; b[tp] = a[i]; } } printf("%d\n", len+num); } signed main() { int T; scanf("%d",&T); for(re i=1;i<=T;++i) { printf("Case #%d: ",i); work(); } return 0; }