题目链接:https://codeforces.com/problemset/problem/360/B
题目大意:
给你一个数组a含n个整数,现在你可以任意改变k个数字,最小化最大值a[i+1] - a[i] (1 <= i <= n-1)。1 <= k <= n <= 2000。
解题思路:
一般最小化最大值可以想到二分,但是怎么检验最小化的最大值是个难办的问题,因为你又不能暴力枚举。看到n和k的范围那么小,能不能用dp检验呢?答案是可以的。考虑dp[i]表示在a[i]不变的情况下,前i个数已经调整好,所需要调整的数的个数。可得转移方程为dp[i] = min(dp[i], dp[j] + (i-j-1)), (1 <= j < i)。(i-j-1)表示a[i]和a[j]之间有(i-j-1)个数,而且这些数都要得到调整。转移需要的条件是(i-j)*k <= abs(a[i] - a[j]), k是需要检验的答案。需要这个条件的原因是a[i]和a[j]之间有(i - j)个间隔,那么a[i]和a[j]在这个检验答案中最多允许相差(i-j) * k,否则无论怎么调整a[i]和a[j]的之间数都无法得出答案的,也就是无法转移状态。
代码如下:
# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n, k;
ll a[2005], dp[2005];
bool check(ll mid){
for(int i = 1; i <= n; ++i){
dp[i] = i-1;
for(int j = 1; j < i; ++j){
if(mid*(i-j) >= abs(a[i] - a[j]))
dp[i] = min(dp[i], dp[j] + i - j - 1);
}
if(k - (n-i) >= dp[i]) return true;
}
return false;
}
int main(){
std::ios::sync_with_stdio(false);
while(cin >> n >> k){
ll l = 0, r = 0;
for(int i = 1; i <= n; ++i){
cin >> a[i];
r = max(r, abs(a[i] - a[i-1]));
}
ll ans = r;
while(l <= r){ //注意加等号!!!
ll mid = (l+r) / 2;
if(check(mid)){
r = mid-1;
ans = mid;
} else {
ans = mid+1;
l = mid+1;
}
}
cout << ans << endl;
}
return 0;
}