简要题意:
长度为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() { num = 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; }View Code