题意:
给出一串序列,其中每个0可以转化成任何数,问转化过后,最长的严格递增子序列的长度。
思路:
贪心的思路很巧妙,首先如果是排在LIS的左右两端的0,都可以不考虑,最后再加上,因为0可以变成任何数。
如果是出现在中间的0,可以这样思考,因为0可以变成任何数,灵活度最高,所以最后能求出的最长上升子序列一定至少有一种情况是包含了所有的0。
既然0肯定会出现在最终的结果LIS中,那么我们不考虑0,直接求其余部分的LIS,最后再加上0的个数不就行了。
但是要注意,既然不考虑0,也要消去0对其余部分的影响,如序列:x,0,0,y,如果不考虑0,也就是说想把两个0都算到最后的LIS结果中,若0之前的LIS末尾为x,这两个0分别算作x+1和x+2,再后面的数至少要是x+3才能再被算入LIS中,如果不考虑0,那么将这之后的所有数都减去之前出现过的0的个数,仍要满足LIS,这样最后的结果也就会出现这些数。
比如:1,2,0,0,4,5,消去0的影响之后是1,2,2,3,这样求出的LIS是3,其实也就是取了1,2,5,这样再加上两个0,最终答案就是5
剩下的就是二分求LIS即可。
代码:
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAXN = 111111;
int a[MAXN], dp[MAXN];
int main() {
int T, cs = 0;
scanf("%d", &T);
while (T--) {
int n, m = 0, cnt = 0, x;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &x);
if (!x) ++cnt;
else a[++m] = x - cnt;
}
fill(dp + 1, dp + 1 + m, INF);
for (int i = 1; i <= m; i++) {
int pos = lower_bound(dp + 1, dp + 1 + m, a[i]) - dp;
dp[pos] = a[i];
}
int ans = lower_bound(dp + 1, dp + 1 + m, INF) - dp - 1;
ans += n - m;
printf("Case #%d: %d\n", ++cs, ans);
}
return 0;
}