传送门
解析:
一看就是二分,然而这个DPDPDP我只会O(n3)O(n^3)O(n3)的区间DpDpDp做法。。。
一看觉得可做,考场上就去推式子去了,结果推挂了。。。
一看题解,均分的思想。。。我去我第一个想的就是这个正解,结果被我莫名其妙的否了啊啊啊啊?!!!!
思路:
其实考虑如果只允许改变一个怎么选择,直接选择差最大的一组中的一个,调整成均分就行了。
比如我们现在有一组数据1,5,13,191,5,13,191,5,13,19,只允许调整一个,那么我们肯定选择555和131313中的一个,调整至这个数的两边均分,显然这里将555调整成777是最优的。
那么现在这里要求调整多个数,怎么办?
显然如果我们能够调整相邻数之差到不超过xxx,那么必然存在方案不超过x+1x+1x+1,即解的存在性是随xxx单调的。考虑二分。
那么怎么验证,考虑上述的均分的思想,如果原数列中abs(ai−aj)<=(j−i)×xabs(a_i-a_j)<=(j-i)\times xabs(ai−aj)<=(j−i)×x,那么我们可以通过调整i<k<ji < k < ji<k<j的所有aka_kak来使得i−ji-ji−j这段区间满足条件,即调整j−i−1j-i-1j−i−1个数。
那么怎么求出需要调整的最少个数?考虑DpDpDp
我们用fif_ifi表示保持iii不变,令区间i−ni-ni−n满足性质需要的最少调整次数。
那么状态转移就很好想了fi=min{n−i,min{fj+j−i−1}(abs(ai−aj)<=(j−i)×x)}f_i=min\{n-i,min\{f_j+j-i-1\}(abs(a_i-a_j)<=(j-i)\times x)\}fi=min{n−i,min{fj+j−i−1}(abs(ai−aj)<=(j−i)×x)}
那么对于每个二分出的xxx,这样DPDPDP一下,再判断是否能够满足不超过kkk次就行了。
代码中的checkcheckcheck用了一个贪心的小剪枝,显然我们可以通过把前i−1i-1i−1个数调整与aia_iai相等,使得满足xxx的限制,这时候就会调整fi+i−1f_i+i-1fi+i−1个数,贪心判断一下就好了。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const
inline int getint(){
re int num;
re char c;
re bool f=0;
while(!isdigit(c=gc()))if(c=='-')f=1;num=c^48;
while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
return f?-num:num;
}
cs int N=2003;
int n,K;
int a[N];
int f[N];
inline bool check(int x){
for(int re i=n;i;--i){
f[i]=n-i;
for(int re j=i+1;j<=n;++j){
if(abs(a[j]-a[i])<=1ll*x*(j-i))f[i]=min(f[i],f[j]+j-i-1);
}
if(f[i]+i-1<=K)return true;
}
return false;
}
int maxn;
signed main(){
n=getint();K=getint();
for(int re i=1;i<=n;++i){
a[i]=getint();
maxn=max(maxn,abs(a[i]-a[i-1]));
}
int l=0,r=maxn;
while(l<r){
int mid=(l+r)>>1;
if(check(mid))r=mid;
else l=mid+1;
}
cout<<l;
return 0;
}