https://codeforces.com/contest/1133/problem/E
题意
给你n个数(n<=5000),你需要对其挑选并进行分组,总组数不能超过k(k<=5000),每组数字差距不超过5,问最多能挑出多少数字
题解
- 先排序,在进行dp,定义dp[i][j]为前i个数分成j组的最大值
- 两个转移方向
- 不选 dp[i-1][j] -> dp[i][j]
- 和前面的分组 dp[lt[i]-1][j-1] -> dp[i][j]
- 怎么确定i前面的哪个点是最大的?
- 选择能和i分到一组的最前面的数
因为选择最前面的数可以降低前一组的上限
- 用双指针or单调队列处理
双指针板子
for(l=r=n;l>=1;){
if(l<=r){
if(a[r]-a[l]<=5)
lt[r]=l--;
else lt[--r]=++l; //l++十分重要,因为可能新的l和新的r不合适,这样就r就会继续向左移动,原来的r并没有找到和他合适的l
}else lt[r]=--l;
}
代码
#include<bits/stdc++.h>
#define M 5005
using namespace std;
long long a[M],n,k,i,j,p,ans=0,dp[M][M],lt[M],l,r;
int main(){
cin>>n>>k;
for(i=1;i<=n;i++){
scanf("%lld",&a[i]);
lt[i]=i;
}
sort(a+1,a+n+1);
for(l=r=n;l>=1;){
if(l<=r){
if(a[r]-a[l]<=5)
lt[r]=l--;
else lt[--r]=++l;
}else lt[r]=--l;
}
for(i=1;i<=n;i++){
for(j=1;j<=min(k,n);j++){
p=lt[i];
dp[i][j]=max(dp[i-1][j],dp[p-1][j-1]+i-p+1);
ans=max(dp[i][j],ans);
}
}
cout<<ans;
}