[SCOI2014]方伯伯的玉米田
题目描述
方伯伯在自己的农田边散步,他突然发现田里的一排玉米非常的不美。这排玉米一共有 \(N\) 株,它们的高度参差不齐。方伯伯认为单调不下降序列很美,所以他决定先把一些玉米拔高,再把破坏美感的玉米拔除掉,使得剩下的玉米的高度构成一个单调不下降序列。方伯伯可以选择一个区间,把这个区间的玉米全部拔高 \(1\) 单位高度,他可以进行最多 \(K\) 次这样的操作。拔玉米则可以随意选择一个集合的玉米拔掉。问能最多剩多少株玉米,来构成一排美丽的玉米。
输入格式
第一行包含两个整数 \(n, K\),分别表示这排玉米的数目以及最多可进行多少次操作。第二行包含 \(n\) 个整数,第 \(i\) 个数表示这排玉米,从左到右第 \(i\) 株玉米的高度 \(a_i\)。
输出格式
输出一个整数,最多剩下的玉米数。
样例 #1
样例输入 #1
3 1
2 1 3
样例输出 #1
3
提示
\(100\%\) 的数据满足:$2 \le N \lt 10^4 $,\(2 \le K \le 500\),\(1 \leq a_i \leq 5000\)。
思路
定义 \(dp[i][j]\) 表示以 \(i\) 结尾,可以拔高 \(j\) 次的最长不下降子序列。
状态转移方程:
\[dp[i][j]=\max_{p,q}^{1 \le p < i,0\le q\le j,a[i]+j\ge a[p]+q} dp[p][q]+1 \]直接暴力转移显然不行,时间复杂度 \(O(n^2k^2)\) ,一分没有。
观察发现每次拔高后最长不下降子序列的长度不会减少,所以可以用二维树状数组维护 \(dp\) 数组,但需要改一下状态。
\(dp[a][b]\) 表示 \([1,i-1]\) 中最多拔高 \(b\) 次,末尾不超过 \(a\) 的最长不下降子序列的长度(类似于 \(n\log n\) 求LIS的 \(low\) 数组)。
把 \(dp\) 数组丢到树状数组里就好了。
时间复杂度 \(O(nk\log n \log k)\) 。
代码
#include<bits/stdc++.h>
using namespace std;
int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
void write(int x){
if(x<0){putchar('-');x=-x;}
if(x>9)write(x/10);
putchar(x%10+'0');
}
int a[10005],f[10005][1005],n,k,maxx,_ans;
//树状数组模板
int lowbit(int x){
return x&(-x);
}
void change(int x,int y,int val){
for(int i=x;i<=maxx+k;i+=lowbit(i))
for(int j=y;j<=k+1;j+=lowbit(j))
f[i][j]=max(f[i][j],val);
}
int ask(int x,int y){
int ans=0;
for(int i=x;i;i-=lowbit(i))
for(int j=y;j;j-=lowbit(j))
ans=max(ans,f[i][j]);
return ans;
}
//---------------
int main(){
n=read(),k=read();
for(int i=1;i<=n;i++)a[i]=read(),maxx=max(maxx,a[i]);
for(int i=1;i<=n;i++){
for(int j=k;j>=0;j--){
int t=ask(a[i]+j,j+1)+1;//找到dp[a[i]+j][j+1]
_ans=max(_ans,t);//更新答案
change(a[i]+j,j+1,t);//放入树状数组
}
}
printf("%d\n",_ans);
return 0;
}
标签:le,int,题解,玉米,玉米田,ch,数组,SCOI2014,dp
From: https://www.cnblogs.com/maniubi/p/16948453.html