zoukankan      html  css  js  c++  java
  • 「单调队列优化DP」P2034 选择数字

    「单调队列优化DP」P2034 选择数字

    题面描述:

    给定一行n个非负整数a[1]..a[n]。现在你可以选择其中若干个数,但不能有超过k个连续的数字被选择。你的任务是使得选出的数字的和最大。
    输入格式

    第一行两个整数n,k

    以下n行,每行一个整数表示a[i]。

    输出格式

    输出一个值表示答案。

    输入输出样例

    输入 #1

    5 2
    1
    2
    3
    4
    5

    输出 #1

    12

    说明/提示

    对于20%的数据,n <= 10

    对于另外20%的数据, k = 1

    对于60%的数据,n <= 1000

    对于100%的数据,1 <= n <= 100000,1 <= k <= n,0 <= 数字大小 <= 1,000,000,000

    时间限制500ms

    解法

    一般正常求序列几段和都要求前缀和,f的第一维都是前i项的最优值

    那我们直接开始吧,

    f[i]=max(f[j])+a[i] ( i-k<=j<i )
    

    然鹅叫上去可能只对两个点(可能连样例都不过),原因是方程都错了,少了一维,i不一定选取就是最佳选择,如1 8 4 2 999 k=2,显然不选4要更优, 所以正确的转移方程:

    //0表示不选第i个数,1表示选第i个数
    
    f[0][i]=max(f[0][i-1],f[1][i-1]);
    
    f[1][i]=max(f[0][j]-sum[j])+sum[i];
    
    

    亲测O(n*n)+快读能压线过

    所以考虑优化

    我们用单调队列维护f[0][j]-sum[j]的最优值,因为它完全符合单调性,维护就完事

    代码:

    /*#!/bin/sh
    dir=$GEDIT_CURRENT_DOCUMENT_DIR
    name=$GEDIT_CURRENT_DOCUMENT_NAME
    pre=${name%.*}
    g++ -O2 $dir/$name -o $pre -g -Wall -std=c++11
    if test $? -eq 0; then
        gnome-terminal -x bash -c "time $dir/$pre;echo;read;"
    fi*/
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    const int maxn=1e6+5,INF=0x3f3f3f3f;
    inline int read(){
    	int s=0,w=1;
    	char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();};
    	while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
    	return s*w;
    }
    int n,d,a[maxn],head=1,tail;
    long long sum[maxn],f[2][maxn],q[maxn];//每个数有1e9诶
    int main(){
    	n=read();d=read();
    	for(int i=1;i<=n;i++)a[i]=read(),sum[i]=sum[i-1]+a[i];
    	f[1][1]=a[1];tail++;//tail=0需要初始化,tail=1就不需要,推荐写tail=0
    	for(int i=2;i<=n;i++){
    		f[0][i]=max(f[0][i-1],f[1][i-1]);//不选第i个数的情况
    		while(head<=tail&&i-q[head]>d)head++;//维护队首,i-k>j(q[head])
    		f[1][i]=f[0][q[head]]-sum[q[head]]+sum[i];
    		while(head<=tail&&f[0][i]-sum[i]>f[0][q[tail]]-sum[q[tail]])tail--;//维护队列单调性,新数大于原数就出队
    		q[++tail]=i;
    	}
    	cout<<max(f[0][n],f[1][n]);
    	
    }
    
  • 相关阅读:
    理解js中的原型链,prototype与__proto__的关系
    Zepto源码(2016)——Zepto模块(核心模块)
    MySQL增删改查
    ACM典型试题--古代密码(二)
    ACM典型试题--简单的加密算法(一)
    MySQL图文安装配置
    (c语言)二叉树中序线索(数据结构十七)
    (C语言)二叉树层次遍历(数据结构十六)
    Java连接db2数据库(常用数据库连接五)
    java连接oracle数据库(常用数据库连接四)
  • 原文地址:https://www.cnblogs.com/614685877--aakennes/p/13197365.html
Copyright © 2011-2022 走看看