CodeForces - 940E - Cashback +贪心+DP

传送门:CodeForces - 940E - Cashback

题意:

在一个长度为n的数组中,可以分出长度为 k 连续的多个数组b(每个数组 b 的 k 可不相同),然后,可以对每个数组 b 进行删去 k / c 个数的操作;

  输出最小的全部数组b的和;

思路:

首先要贪心的想到,这个 k 要么等于 c ,要么等于 1 ,才能使总和最小;

   所以列出递推方程:$hh [ i -1 ] = min(hh[i - 1] + a[ i ]   , hh[ i - c]+sum[ i ] - sum[ i - c] - (数组b中的最小值)  )$;

    其中数组b 中的最小值可以用线段树或(dp+位运算的RMQ)实现;

  下面我用(dp+位运算的RMQ)实现;

#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
#include <cstdio>
using namespace std;

const int maxn = 100000 + 5;
int n, c;
int a[maxn];
int dp[maxn][30];
long long sum[maxn], hh[maxn]; //这里要注意数据范围
void rmq_init() {
    for (int i = 1; i <= n; i++)
        dp[i][0] = a[i];

    for (int j = 1; (1 << j) <= n; j++) {
        for (int i = 1; i + (1 << j) - 1 <= n; i++)
            dp[i][j] = min(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]);
    }
}
int rmq(int l, int r) {
    int len = r - l + 1;
    int k = 0;
    while ((1 << (k + 1)) <= len) {
        k++;
    }
    return min(dp[l][k], dp[r - (1 << k) + 1][k]);
}
int main() {
    scanf("%d%d", &n, &c);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        sum[i] = sum[i - 1] + a[i];
    }
    rmq_init();
    for (int i = 1; i <= n; i++) {
        if (i >= c) {
            long long tmp = hh[i - c] + sum[i] - sum[i - c] - rmq(i - c + 1, i);
            hh[i] = hh[i - 1] + a[i] > tmp ? tmp : hh[i - 1] + a[i];
        } else
            hh[i] = hh[i - 1] + a[i];
    }
    printf("%lld\n", hh[n]);
    return 0;
}
View Code

 

posted @ 2018-03-04 14:54  ckxkexing  阅读(104)  评论(0编辑  收藏  举报