传送门
解析:
一看就是二分,然而这个
D
P
DP
DP我只会
O
(
n
3
)
O(n^3)
O(n3)的区间
D
p
Dp
Dp做法。。。
一看觉得可做,考场上就去推式子去了,结果推挂了。。。
一看题解,均分的思想。。。我去我第一个想的就是这个正解,结果被我莫名其妙的否了啊啊啊啊?!!!!
思路:
其实考虑如果只允许改变一个怎么选择,直接选择差最大的一组中的一个,调整成均分就行了。
比如我们现在有一组数据 1 , 5 , 13 , 19 1,5,13,19 1,5,13,19,只允许调整一个,那么我们肯定选择 5 5 5和 13 13 13中的一个,调整至这个数的两边均分,显然这里将 5 5 5调整成 7 7 7是最优的。
那么现在这里要求调整多个数,怎么办?
显然如果我们能够调整相邻数之差到不超过 x x x,那么必然存在方案不超过 x + 1 x+1 x+1,即解的存在性是随 x x x单调的。考虑二分。
那么怎么验证,考虑上述的均分的思想,如果原数列中 a b s ( a i − a j ) < = ( j − i ) × x abs(a_i-a_j)<=(j-i)\times x abs(ai−aj)<=(j−i)×x,那么我们可以通过调整 i < k < j i < k < j i<k<j的所有 a k a_k ak来使得 i − j i-j i−j这段区间满足条件,即调整 j − i − 1 j-i-1 j−i−1个数。
那么怎么求出需要调整的最少个数?考虑 D p Dp Dp
我们用
f
i
f_i
fi表示保持
i
i
i不变,令区间
i
−
n
i-n
i−n满足性质需要的最少调整次数。
那么状态转移就很好想了
f
i
=
m
i
n
{
n
−
i
,
m
i
n
{
f
j
+
j
−
i
−
1
}
(
a
b
s
(
a
i
−
a
j
)
<
=
(
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)}
那么对于每个二分出的 x x x,这样 D P DP DP一下,再判断是否能够满足不超过 k k k次就行了。
代码中的 c h e c k check check用了一个贪心的小剪枝,显然我们可以通过把前 i − 1 i-1 i−1个数调整与 a i a_i ai相等,使得满足 x x x的限制,这时候就会调整 f i + i − 1 f_i+i-1 fi+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;
}