题目传送门
题目描述:
你有一个长度为 n个序列 a,其中,值为0 的元素可以被修改成为任意一个 整数。你需要最大化这个序列的最长上升子序列长度。
Solution
我们来考虑贪心做法。
首先,我们的最优解一定是包含所有的0的,为什么?
对于一串数,它其中的一个数能够用0代替,而其中没有的数则可以用0构造,也就是说我们多用0就能够有更多的机会(不服的人大可举一举是否有反例。其实我也不清楚 )
那么对于两个数 a [ i ] a[i] a[i]和 a [ j ] a[j] a[j],怎么样才能把他们之间的0全部选上呢??
- 我们设 s u m [ i ] 表 示 前 i 个 数 中 0 的 个 数 sum[i]表示前i个数中0的个数 sum[i]表示前i个数中0的个数
- 如果 a [ j ] − a [ i ] > s u m [ j ] − s u m [ i ] a[j]-a[i]>sum[j]-sum[i] a[j]−a[i]>sum[j]−sum[i],那么他们之间的0就是可以选上的
- 移项得; a [ j ] − s u m [ j ] > a [ i ] − s u m [ j ] a[j]-sum[j]>a[i]-sum[j] a[j]−sum[j]>a[i]−sum[j]
- 咦?这样的式子好像有点眼熟??
- 没错,令 a [ i ] = a [ i ] − s u m [ i ] a[i] = a[i]-sum[i] a[i]=a[i]−sum[i],然后做一遍LIS即可
Code
#include<bits/stdc++.h>
using namespace std;
int n;
int a[10101000];
int f[1010101];
int t,cnt = 0 , len = 0;
void work(){
int num = 0 , num0 = 0;
scanf("%d",&n);
for (int i=1,x;i<=n;i++){
scanf("%d",&x);
if (x == 0) num0++;
else a[++num] = x-num0;
}
if (num0 == n){
printf("Case #%d: %d\n",cnt,n);
return;
}
// for (int i=1;i<=n;i++) cout<<a[i]<<' ';
// cout<<endl;
len = 1;
f[1] = a[1];
for (int i=2;i<=num;i++){
if (a[i] > f[len]) f[++len] = a[i];
else {
int pos = lower_bound(f+1,f+len+1,a[i])-f;
f[pos] = a[i];
}
}
// cout<<len<<' '<<sum[n]<<endl;
printf("Case #%d: %d\n",cnt,len + num0);
return;
}
int main(){
scanf("%d",&t);
while (t--) ++cnt,work();
}