Codeforces Round #544 (Div. 3) dp + 双指针

https://codeforces.com/contest/1133/problem/E

题意

给你n个数(n<=5000),你需要对其挑选并进行分组,总组数不能超过k(k<=5000),每组数字差距不超过5,问最多能挑出多少数字

题解

  • 先排序,在进行dp,定义dp[i][j]为前i个数分成j组的最大值
  • 两个转移方向
    1. 不选 dp[i-1][j] -> dp[i][j]
    2. 和前面的分组 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;
}
原文地址:https://www.cnblogs.com/VIrtu0s0/p/10502722.html