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