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\n", dp[n]);

    return 0;
}

原文地址:https://www.cnblogs.com/sbfhy/p/8590868.html

时间: 2024-08-30 07:01:03

codeforces 940E 思维,dp的相关文章

Queue CodeForces - 353D (思维dp)

https://codeforces.com/problemset/problem/353/D 大意:给定字符串, 每一秒, 若F在M的右侧, 则交换M与F, 求多少秒后F全在M左侧 $dp[i]$为位置$i$处的$F$复位所花费时间, 有 $dp[i] = max(dp[i-1]+1,cnt_i)$, $cnt_i$为前$i$位$M$的个数 $dp$最大值即为答案 #include <iostream> #include <algorithm> #include <cstd

Codeforces 13C Sequence --DP+离散化

题意:给出一个 n (1 <= n <= 5000)个数的序列 .每个操作可以把 n 个数中的某一个加1 或 减 1.问使这个序列变成非递减的操作数最少是多少 解法:定义dp[i][j]为将前i个数变为以j为结尾的非递减序列的最少操作次数. 则有: dp[i][j] = min(dp[i][j], min(dp[i][k]) + Cost(原来第i个位置上的数转换到j))  (1 <= k <= j) 即前i个数以j结尾的状态可以由前i-1个数以小于等于j的k结尾的状态转移过来,取

Codeforces 77C 树形dp + 贪心

题目链接:点击打开链接 题意: 给定n个点, 每个点的豆子数量 下面是一棵树 再给出起点 每走到一个点,就会把那个点的豆子吃掉一颗. 问:回到起点最多能吃掉多少颗豆子 思路:树形dp 对于当前节点u,先把子节点v都走一次. 然后再往返于(u,v) 之间,直到u点没有豆子或者v点没有豆子. dp[u] 表示u点的最大值.a[u] 是u点剩下的豆子数. #include <cstdio> #include <vector> #include <algorithm> #inc

Codeforces 57C Array dp暴力找规律

题目链接:点击打开链接 先是计算非递增的方案, 若非递增的方案数为x, 则非递减的方案数也是x 答案就是 2*x - n 只需求得x即可. 可以先写个n3的dp,然后发现规律是 C(n-1, 2*n-1) 然后套个逆元即可. #include<iostream> #include<cstdio> #include<vector> #include<string.h> using namespace std; #define ll long long #def

Codeforces 413D 2048(dp)

题目连接:Codeforces 413D 2048 题目大意:2048的游戏,两个相同的数x可以变成一个2*x,先给出n,表示在一个1*n的矩阵上面玩2048,规定每次向左移动,并且每次出现一个,给出序列n,表示出现的块的值,0表示既可以是2也可以是4,问说有多少种可能,使得游戏结束后的最大块的值大于等于2^k. 解题思路:dp[i][j][x]表示第i个位置,值为j,x表示先前有没有出现过大于2^k的数: 这种递增的情况可以直接表示为14(总和,以为后面的2,4如果变大,就肯定能和8想合在一起

Codeforces 455A Boredom (dp)

很裸的dp 状态转移方程 dp[i]=max(dp[i-1],dp[i-2]+dp[i]*i) #include<bits/stdc++.h> using namespace std; long long dp[100020]; int main() { int n,a; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&a); dp[a]++; } for(int i=2;i&

Codeforces 176B 经典DP

非常好的一个题目,CF上的DP都比较经典 题意就是 给定一个串A,B,正好执行K次操作,每次操作可以把 A串从中间切开,并调换两部分的位置,问最后得到B串共有多少种不同的切法(只要中间有一次不同,即视为不同) 首先,题目的一个关键点一定要抓到,就是 ,不管怎么切 然后调换位置,其实串根本没变,你把串想成一个环,从某一点分成两部分并且交换位置,其实就是把串的起点变到了该点,这是很关键也是最机智的一点 然后,我们要发现规律,你纸上模拟也行,推理也行.. 我们发现:1.首先原串(即以0号字母开头的)个

Nanami&#39;s Digital Board CodeForces - 434B (棋盘dp)

大意: 给定01矩阵, m个操作, 操作1翻转一个点, 操作2求边界包含给定点的最大全1子矩阵 暴力枚举矩形高度, 双指针统计答案 #include <iostream> #include <algorithm> #include <math.h> #include <cstdio> #include <set> #include <map> #include <string> #include <vector>

Codeforces 1088E 树形dp+思维

比赛的时候看到题意没多想就放弃了.结果最后D也没做出来,还掉分了,所以还是题目做的太少,人太菜. 回到正题: 题意:一棵树,点带权值,然后求k个子连通块,使得k个连通块内所有的点权值相加作为分子除以k的值最大,如果这样的最大值有多个,就最大化k. 赛后看了看别人的代码仔细想了一想,还是挺容易的. 首先将树分为若干个连通块,考虑一个权值求和最大的连通块,设该最大值为sum,那么其他所有的连通块,权值求和都不可能比当前的连通块大. 在不考虑最大化k的情况下,就没有必要再将其他的连通块加进来(因为如果