NOIP提高模拟-20181024-T1-小C的数组

本文介绍了一种结合二分查找与动态规划(DP)的方法,用于解决求序列相邻两项差值最大值最小化的难题。通过将问题简化为验证单一数值,再逐步扩展至整个序列,实现了有效验证策略。文章详细阐述了状态转移方程,并提供了O(N^2logN)时间复杂度的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

写在前面

考试的时候并没有想出来二分以后如何用DPDPDP验证,所以随手敲了个DPDPDP然后愉快爆零。

Solution

首先按照题面不难想到,这是一个求序列相邻两项的差的最大值最小的问题,那么显然考虑二分。
在二分以后如何去验证呢?
我们可以这样想:对于一个序列,可能不好直接去验证,那么考虑验证一个短一点的,比如说只有一个数,那么这玩意儿行不行是可以直接算出来的。在这个基础上进行扩展,不难想到DPDPDP是可以用来验证的,因为我们需要扩展状态。
fif_ifi表示iii位置不被修改,而他之后的数全部满足小于等于midmidmid所需要的最少修改次数,那么我们在转移状态的时候只需要枚举下一个没有被修改的位置jjj,若iiijjj中所有数被修改后全部满足二分值midmidmid,则,
(j−i)∗mid≥∣Aj−Ai∣ \left(j-i\right)*mid \geq \left| A_j-A_i \right| (ji)midAjAi
所以说总的转移方程为
f[i]=min(f[i],f[j]+j−i−1)      (∣a[j]−a[i]∣&lt;=mid∗(j−i)) f\left[i\right]= min(f\left[i\right],f\left[j\right]+j-i-1)\ \ \ \ \ \ (\left|a[j]-a[i]\right|&lt;=mid*(j-i)) f[i]=min(f[i],f[j]+ji1)      (a[j]a[i]<=mid(ji))
时间复杂度O(N2logN)O(N^2logN)O(N2logN)
代码如下:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
template <typename T>
void read(T &x){
    x=0;
    T neg=1;
    static char c=getchar();
    while(!isdigit(c)&&c!='-') c=getchar();
    if(c=='-') neg=-1,c=getchar();
    while(isdigit(c)) x=(x<<1)+(x<<3)+c-'0',c=getchar();
    x*=neg;
}
ll n,k,a[2005];
ll f[2005];
bool check(ll mid){
    for(int i=n;i>=1;i--){
        f[i]=n-i;
        for(int j=i+1;j<=n;j++)
            if(abs(a[j]-a[i])<=mid*(ll)(j-i)) f[i]=min(f[i],f[j]+j-i-1);
        if(f[i]+i-1<=k) return true;
    }
    return false;
}
int main(){
    read(n); read(k);
    for(int i=1;i<=n;i++) read(a[i]);
    int l=0,r=2e9,ans=r;
    while(l<=r){
        int mid=(l+r)>>1;
        if(check(mid)) r=mid-1,ans=mid;
        else l=mid+1;
    }
    printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值