codeforces 361 D. Levko and Array(dp+二分)

题目链接: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;
}
posted @ 2017-05-09 22:36  Gealo  阅读(228)  评论(0编辑  收藏  举报