zoukankan      html  css  js  c++  java
  • 斜率dp A

    A - Print Article

     HDU - 3507 

    今天刚刚学习了一下斜率dp,感觉还ok,主要就是要推这个斜率,然后利用数据结构来优化。

    推荐两篇写的比较好的博客,https://www.cnblogs.com/orzzz/p/7885971.html ----> 这个主要学习这个斜率dp的思路

    https://www.cnblogs.com/ka200812/archive/2012/08/03/2621345.html ---> 这个主要看代码,中间过程感觉有点问题。

    https://blog.csdn.net/qq_37025443/article/details/78986673  --->  这个也建议看看

    自己重新理一下这个思路。

    sum[i] 表示从1~i 的前缀和

    dp[i]表示输到第 i 个字母的最小的花费

    所以转移方程很简单 dp[i]=min(dp[j]+(sum[i]-sum[j])^2+M),因为这个j已经输出了,所以下次考虑j+1~i 所以是sum[i]-sum[j] 而不是sum[i]-sum[j-1]

    因为这个n有5e5 所有for两次显然会T,这个就可以利用斜率来优化。

    令j>k 如果从 j 转移比从k转移更优就需要满足  dp[j]+(sum[i]-sum[j])^2+M<dp[k]+(sum[i]-sum[k])^2+M

    上面式子化简之后就是dp[j]+sum[j-1]^2-(dp[k]+sum[k]^2)<2*sum[i]*(sum[j-1]-sum[k])

    然后令F[x]=dp[x]-sum[x]^2 

    所以就是F[j]-F[k]<sum[i]*2*(sum[j]-sum[k]) 而且要满足  j>k

    令G[j,k]=(F[j]-F[k])/(2*(sum[j]-sum[k])

    所以如果有 i>j>k 那么如果是G[i,j]>G[j,k] 这样就是一个上凹折线,这个肯定不对

    G[i,j]<sum[i],那么就是说i点要比j点优,排除j点。

    如果G[i,j]>=sum[i],那么j点此时是比i点要更优,但是同时G[j,k]>G[i,j]>sum[i]。这说明还有k点会比j点更优,同样排除j点。

    所以一定是下凸的折现,如果不是的就可以排除。

    然后对于G[i,j]>G[j,k]

    也有三种情况:

    1. G[i,j]>sum[t]>G[j,k]   j 比 i 优,j 比 k 优

    2. G[i,j]>G[j,k]>sum[t]  j 比 i 优,k比 j 优

    3.sum[t]>G[i,j]>G[j,k]  i 比 j 优 ,j比 k 优

    所以我们就用单调队列维护<sum[t] 的最大值即可。

    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    #include <algorithm>
    #include <queue>
    #include <vector>
    #include <string>
    #define inf 0x3f3f3f3f
    #define inf64 0x3f3f3f3f3f3f3f3f
    using namespace std;
    const int maxn = 5e5 + 10;
    
    int que[maxn], dp[maxn];
    int sum[maxn];
    int n, m;
    
    int up(int i,int j)
    {
        return dp[i] + sum[i] * sum[i] - (dp[j] + sum[j] * sum[j]);
    }
    
    int down(int i,int j)
    {
        return 2 * (sum[i] - sum[j]);
    }
    
    int DP(int i,int j)
    {
        return dp[j] + (sum[i] - sum[j])*(sum[i] - sum[j]) + m;
    }
    
    int main()
    {
        while(scanf("%d%d",&n,&m)!=EOF)
        {
            sum[0] = dp[0] = 0;
            for (int i = 1; i <= n; i++) scanf("%d", &sum[i]), sum[i] += sum[i - 1];
            int tail = 0, head = 0;
            que[tail++] = 0;//因为可能是前面i个全部作为一段才是最小值
            for (int i = 1; i <= n; i++) {//head+1<tail 保证队列里面至少有两个值
                while (head + 1 < tail&&up(que[head + 1], que[head]) <= sum[i] * down(que[head + 1], que[head])) head++;
                dp[i] = DP(i, que[head]);
                while (head + 1 < tail&&up(i, que[tail - 1])*down(que[tail - 1], que[tail - 2]) <= up(que[tail - 1], que[tail - 2])*down(i, que[tail - 1])) tail--;
                que[tail++] = i;
            }
            printf("%d
    ", dp[n]);
        }
        return 0;
    }

      

  • 相关阅读:
    变量的含义,内存管理机制,数据类型的含义
    操作系统、应用程序的含义,编程语言的优缺点
    Pycharm常用快捷键
    计算机组成及原理
    仿酷狗音乐播放器开发日志——整体框架分析
    仿酷狗音乐播放器开发日志——整体框架分析
    c++与java的优缺点
    c++与java的优缺点
    unicode ansi utf-8 unicode_big_endian编码的区别
    unicode ansi utf-8 unicode_big_endian编码的区别
  • 原文地址:https://www.cnblogs.com/EchoZQN/p/11396635.html
Copyright © 2011-2022 走看看