zoukankan      html  css  js  c++  java
  • 题解 P4852 【yyf hates choukapai】

    (Large exttt{P4852})

    联赛前的一篇 ( exttt{DP}) 题解,祝各位不要挂分。

    标签:单调队列优化 ( exttt{DP})

    这也是我第一次写单调队列题解,写的不好请见谅QwQ。

    并输出选择方案。

    题意

    一段序列,你要在序列里选择 (n) 段长 (c) 的子序列,价值为序列第一个数,还要选 (m) 个数,价值为它本身,求最大的价值和,但是不能选连续 (d) 个数,求最大价值和。

    (1 le n le 40)(1 le d le m le 80000)(2 le c le 3000)

    保证有解。

    思路

    看上去不这么显然观察到 (n) 只有 (40) ,可能是一个与 (n) 密切有关的 ( exttt{DP})

    先建立 ( exttt{dp[i]}) ,表示前 (i) 个数的最大价值,如果普通暴力转移,时间会爆炸并且这个方程有后效性的(即前i个数末尾有几个单选的数可能会影响到后面的选择)。

    考虑二维 ( exttt{DP}) ,建立( exttt{dp[i][j]}) 表示前 (i) 个数有 (j) 个长为 (c) 的子序列,很容易得到转移方程,每一步都 (j-1) 转移上来。

    可以列出状态转移方程:

    [ exttt{dp[i][j]=dp[k-c][j-1]+sum[i]-sum[k]+a[k-c+1]} ]

    其中,方案为在 (k-c+1)(k) 选一段长为 (c) 序列,在 (k+1)(i)(i-k) 个单个的数。

    (k) 的范围十分显然,(i-d le k le i)

    但是一算复杂度直接炸了,考虑优化。


    观察式子,发现没有带 (k) 的项与带 (i)的项相关联,可以把sum[i]单独提出来,剩下的全和 (k) 有关。

    单调队列可做。

    每次将式子入单调队列,并排除 (k) 过小的情况可以做到 ( exttt{O(1)}) 转移。

    方案的计算就再开一维记录每一个状态从何而来。

    总复杂度 (large exttt{O(n(nc+m))})

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    #define int long long
    const int N = 1e6;
    const int M = 40;
    inline int read()
    {
        register int s = 0;
        register bool neg = 0;
        register char c = getchar();
        for (; c < '0' || c > '9'; c = getchar())
            neg |= (c == '-');
        for (; c >= '0' && c <= '9'; s = s * 10 + (c ^ 48), c = getchar())
            ;
        return (neg ? -s : s);
    }
    
    int a,b,c,d,mx,dp[N+10][M+10][2],s[N+10],sum[N+10],qu[N+10][2],l,r;
    
    inline int calc(int n,int m) {
        return dp[n-c][m][0]-sum[n]+s[n-c+1];
    }
    
    inline void print(int n,int m) {
        if(m==-1) return;
        print(dp[n][m][1],m-1);
        printf("%lld ",n+1);
    }
    
    signed main()
    {
        // freopen("in1.in", "r", stdin);
        a=read();
        b=read();
        c=read();
        d=read();
        mx=a*c+b;
        for(int i=1;i<=mx;i++) s[i]=read(),sum[i]=s[i]+sum[i-1];
        for(int i=1;i<=d;i++) dp[i][0][0]=sum[i];
        for(int i=1;i<=a;i++) {
            l=1,r=0;
            for(int j=i*c;j<=mx;j++) {
                while(l<=r&&qu[r][0]<calc(j,i-1)) r--;
                qu[++r][0]=calc(j,i-1);
                qu[r][1]=j;
                while(l<=r&&qu[l][1]<j-d) l++;
                dp[j][i][0]=qu[l][0]+sum[j];
                dp[j][i][1]=qu[l][1]-c;
            }
        }
        printf("%lld
    ",dp[mx][a][0]);
        print(dp[mx][a][1],a-1);
        return 0;
    }
    
  • 相关阅读:
    Windows 平台下的Mysql集群主从复制
    IOS 的loadView 及使用loadView中初始化View注意的问题。(死循环并不可怕)
    【2013625】K2+SAP集成应用解决方案在线研讨会
    to_char 和 to_date 经验分享
    Java向Access插入数据
    spring Bean的生命周期管理
    [置顶] 30分钟,让你成为一个更好的程序员
    Spring框架中Bean的生命周期
    Box2D的相关知识
    第八周项目二
  • 原文地址:https://www.cnblogs.com/RedreamMer/p/13930211.html
Copyright © 2011-2022 走看看