• 20190630模拟赛B(单调队列优化dp)


    。dp无疑了其实。

    在考场上,我写了一个错解,但是数据小都能过,只是会爆空间,考场上想着怎么用滚动数组优化来着。。。。把错解的方程列出来吧

    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=k;j++)
        {
            if(j!=0)
            dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]+a[i]);//dp[i][j]表示前i个里,第j个选不选
            else
            dp[i][j]=ans;
            ans=max(dp[i][j],ans);
        }
    }

    很明显错的了。但是回家发现离正解也差得不远了。我一开始已经想到了前缀和,但是不知道哪里出了点问题。

    总结错误原因:空间炸了。观察方程,其实除了的小问题在这里:我明明可以把一整段加起来,但是却用了单个的加,这就导致了时空的炸裂。

    于是改造dp数组:还是前i个点,但是第二个改造为j为断点,所以dp表示前i个点在j断时的最大值,方程:
    dp[i]=max(dp[i],dp[j-1]+a[j]+...+a[i]);

    dp类似一个前缀和吧。可以把后面一大串的a[j]+...+a[i]用前缀和维护,就变成了
    dp[i]=max(dp[i],dp[j-1]+sum[i]-sum[j]);

    所以,dp里面的值只和一个变量有关,所以可以使用(我及其不熟练的)单调队列进行维护。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=100010;
    int n,m;
    int a[maxn];
    int sum[maxn];
    int dp[maxn];
    int que[maxn];//数组模拟队列
    int d[maxn];//值
    int h=0,t=1;
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            sum[i]=sum[i-1]+a[i];//前缀和
        }
        for(int i=1;i<=n;i++)
        {
            d[i]=dp[i-1]-sum[i];//首先,队尾是当前值
            while(h<=t&&d[que[t]]<d[i])t--;//代入方程式,判断最优解,弹掉不合适的
            que[++t]=i;//放入一个可能最优的值
            while(h<=t&&que[h]<i-m)h++;//再更新
            dp[i]=d[que[h]]+sum[i];//更新状态
        }
        printf("%d",dp[n]);
        return 0;
    }

    估计有很多讲的不好不对,我自己都还有点懵呢,希望大佬指正。

    (我恨dp)

  • 相关阅读:
    C#——面向对象,常用类
    C#——基础
    C#——winform
    Oracle——PL/SQL,存储过程/函数,java连接Oracle操作存储过程/函数,触发器
    Oracle——数据处理(增删改查),创建表与管理表,视图,索引,同义词
    Oracle——集合运算
    PoisonTap
    在Ubuntu上安装LAMP(Apache、Mysql、Php)
    "Unable to locate package lrzsz"的解决办法
    OtterCTF
  • 原文地址:https://www.cnblogs.com/ajmddzp/p/11111986.html
Copyright © 2020-2023  润新知