题目链接: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 " , ans); return 0; }