题目链接:http://codeforces.com/contest/361/problem/D
题意:最多可以修改K次数字,每次修改一个数字变成任意值,C=max(a【i+1】-a【i】);求操作之后最小的C.
题解:由于n和k比较小其实可以考虑一下区间dp,但是如果区间dp要求的话估计是要3维的显然会炸掉。
于是可以考虑一下二分一下结果c的值,为什么要考虑二分呢?主要是由于c的值与修改的次数是成正比的
显然改的越多c值肯定越少,所以可以二分。然后就是如何判断是否满足条件了,这里要用到dp,设dp[i]
表示a[i]不变i之前的数列满足条件最少要改几次。那么转移方程式为:
dp[i]=min(dp[i] , dp[j] + i - j - 1)(表示改动j + 1~i - 1之间的数当然能改动也是需要条件的只有
abs(a[i] - a[j]) <= (i - j) * num时,num表示二分的值如果比num大那么怎么改都没有用)
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#define inf 0X3f3f3f3f
using namespace std;
const int M = 2e3 + 10;
typedef long long ll;
ll a[M] , n , k , dp[M];
bool Is(ll num) {
memset(dp , inf , sizeof(dp));
dp[1] = 0;
for(int i = 2 ; i <= n ; i++) {
dp[i] = i - 1;
//注意这里j一定要从i-1开始。自行理解一下
for(int j = i - 1 ; j >= 1 ; j--) {
if(abs(a[i] - a[j]) <= (i - j) * num) {
dp[i] = min(dp[i] , dp[j] + (ll)(i - j - 1));
}
}
if(dp[i] + n - (ll)i <= k) return true;
}
if(dp[n] <= k) return true;
return false;
}
int main() {
scanf("%I64d%I64d" , &n , &k);
for(int i = 1 ; i <= n ; i++) {
scanf("%I64d" , &a[i]);
}
ll sum = 0;
for(int i = 2 ; i <= n ; i++) {
sum = max(sum , abs(a[i] - a[i - 1]));
}
ll l = 0 , r = sum;
ll ans = sum;
while(l <= r) {
ll mid = (l + r) >> 1;
if(Is(mid)) {
ans = min(ans , mid);
r = mid - 1;
}
else {
l = mid + 1;
}
}
printf("%I64d\n" , ans);
return 0;
}