codeforces 940E 思维,dp

E. Cashback

题意:
给出常数 c ,定义长度为 n 的序列的价值为:舍去前 (floor)( n/c ) 个最小的数,余下的数的和。
n 个数的序列,要你把它们分隔成多个序列,顺序不能改变。最后的答案为 所有序列的价值的和,求最小的答案。
tags:
我们可以推测出:最后最优的答案,序列长度一定是 c 或 1 。因为如果有 k*c 长度的序列,我们把它分成 k 个长度为 c 的最优; 如果有 k*c+m 的长度序列,那么最后分出 m 个长度为 1 的序列也是更优。
所以就直接 dp 递推: dp[i] 表示前 i 个数可能的最小的价值和。
dp[i] = min(dp[i-1]+a[i] , dp[i-c]+sum[i]-sum[i-c]+get_min(i-c+1, i) ) 。

//  940 E
#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define MP make_pair
#define PB push_back
#define fi  first
#define se  second
#define  mid  (l+(r-l)/2)
typedef long long ll;
const int N = 200005;

int n, C;
ll  tr[N<<2], sum[N], a[N], dp[N];
void build(int ro, int l, int r)
{
    if(l==r) { scanf("%lld", &a[l]);  tr[ro]=a[l];  return ; }
    build(ro<<1, l, mid);  build(ro<<1|1, mid+1, r);
    tr[ro] = min(tr[ro<<1], tr[ro<<1|1]);
}
ll  query(int ro, int l, int r, int ql, int qr)
{
    if(ql<=l && r<=qr) return tr[ro];
    ll  ret = 1e18;
    if(ql<=mid) ret = min(ret, query(ro<<1, l, mid, ql, qr));
    if(mid<qr)  ret = min(ret, query(ro<<1|1, mid+1, r, ql, qr));
    return ret;
}
int main()
{
    scanf("%d%d", &n, &C);
    build(1, 1, n);
    rep(i,1,n) sum[i]=sum[i-1]+a[i];
    rep(i,1,n)
    {
        dp[i] = dp[i-1]+a[i];
        if(i-C>=0) dp[i] = min(dp[i], dp[i-C]+sum[i]-sum[i-C]-query(1,1,n,i-C+1,i));
    }
    printf("%lld
", dp[n]);

    return 0;
}
原文地址:https://www.cnblogs.com/sbfhy/p/8590868.html