zoukankan      html  css  js  c++  java
  • CF940E Cashback

    题目描述

    把一个序列分成若干段,若一段的长度为 (k),则把那段中前 (lfloorfrac{k}{c} floor) 小的数删去, (c) 为给定常数,最小化每段和。

    解法

    显然每段越短越好,因为如果把一个段增大的话,段内的最小值可能减小,而删去的数的总数也可能减少。还有一个例子也可以说明,即两个小区间分别的最小值之和显然比这两个区间的并的大区间的前两个最小值之和大,从而使得最后的答案更小。

    而对于长度小于 (c) 的区间对答案是没有正向贡献的,于是每次取长度为 (c) 的一段是最优的,那么有

    [dp_i=max{dp_{i-1},dp_{i-c}+min{a_j| jin(i-c,i] }}quad iin[c,n] ]

    (dp_i) 表示把前 (i) 个数分成若干段,每段的最小值之和的最大值。那么方程的两项分别对应断开区间和不断。

    (i) 的值是连续的,后面那一堆显然可以用单调队列优化掉,那么最后的复杂度就是 (Theta(n))

    #include<stdio.h>
    #define N 100007
    #define ll long long
    
    template<class T>
    inline void read(T &x){
    	x=0; bool flag=0; char c=getchar();
    	while(c<'0'||c>'9'){if(c=='-') flag=1;c=getchar();}
    	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
    	x=flag? -x:x;
    }
    
    inline ll max(ll x,ll y){return x>y? x:y;}
    
    int n,c,Q[N];
    ll sum=0,a[N],dp[N];
    int main(){
    	read(n),read(c);
    	for(int i=1;i<=n;i++)
    		read(a[i]),sum+=a[i];
    	int l=1,r=0;
    	for(int i=1;i<=n;i++){
    		while(l<=r&&a[Q[r]]>=a[i]) r--;
    		Q[++r]=i;
    		while(l<=r&&Q[l]<=i-c) l++;
    		if(i>=c) dp[i]=max(dp[i-1],dp[i-c]+a[Q[l]]);
    	}
    	printf("%lld",sum-dp[n]);
    }
    
  • 相关阅读:
    JAVA网络编程入门
    悲观锁和乐观锁
    原子性---Atomic
    volatile关键字
    leetcode_111. 二叉树的最小深度
    leetcode_110. 平衡二叉树
    leetcode_108. 将有序数组转换为二叉搜索树
    leetcode_107. 二叉树的层次遍历 II
    leetcode_104. 二叉树的最大深度
    leetcode_101. 对称二叉树
  • 原文地址:https://www.cnblogs.com/wwlwQWQ/p/13848377.html
Copyright © 2011-2022 走看看