• 斜率优化板题 HDU 3507 Print Article


    • 题目大意:输出N个数字a[N],输出的时候可以连续的输出,每连续输出一串,它的费用是 “这串数字和的平方加上一个常数M”。n<=500000
    • 我们设dp[i]表示输出到i的时候最少的花费,sum[i]表示从a[1]到a[i]的数字和。于是方程就是: dp[i]=dp[j]+M+(sum[i]-sum[j])^2;
    • 很显然这个是一个二维的。题目的数字有500000个,不用试了,二维铁定超时了。那我们就来试试斜率优化吧,看看是如何做到从O(n^2)复杂度降到O(n)的。
    • 我们假设k<j<i。如果在j的时候决策要比在k的时候决策好,那么也是就是dp[j]+M+(sum[i]sum[j])2&lt;=dp[k]+M+(sum[i]sum[k])2dp[j]+M+(sum[i]-sum[j])^2 &lt;= dp[k]+M+(sum[i]-sum[k])^2。(因为是最小花费嘛,所以优就是小于)
    • 两边移项一下,得到:
      (dp[j]+sum[j]2)(dp[k]+sum[k]2)&lt;=(sum[j]sum[k])2sum[i](dp[j]+sum[j]^2)-(dp[k]+sum[k]^2) &lt;= (sum[j]-sum[k])*2*sum[i]
      (sum[j]sum[k])(sum[j]-sum[k])除过去,得到:
      [(dp[j]+sum[j]2)(dp[k]+sum[k]2)]/(sum[j]sum[k])&lt;=2sum[i][(dp[j]+sum[j]^2)-(dp[k]+sum[k]^2)]/(sum[j]-sum[k]) &lt;= 2*sum[i]
    • xxi =dp[i]sum[i]= dp[i]-sum[i]2, yyi =2sum[i]= 2*sum[i].
      那么不就是yyjy-yk/x/xjx-xk&lt;=2sum[i]&lt;=2*sum[i]么? 左边是不是斜率的表示?
      那么(y(yjy-yk)/(x)/(xjx-xk)&lt;=2sum[i])&lt;=2*sum[i]说明了什么呢?
      说明k[j,k]=(yk[j,k]=(yjy-yk)/(x)/(xjx-xk)&lt;=2sum[i])&lt;=2*sum[i]代表这j的决策比k的决策要更优。
    • 关键的来了:若k&lt;j&lt;ik&lt;j&lt;ik[i,j]&lt;k[j,k]k[i,j]&lt;k[j,k],则j点永远不可能成为最优解,可以直接将它踢出我们的最优解集。为什么呢?
      分三种情况讨论:
      设当前点为a
      1.如果k[i,j]k[i,j]k[j,k]k[j,k]均小于2sum[a]2*sum[a],则i比j优,j比k优
      2.如果k[i,j]k[i,j]k[j,k]k[j,k]均大于2sum[a]2*sum[a],则k比j优,j比i优
      3.如果k[i,j]&lt;sum[a]k[i,j]&lt;sum[a]k[i,j]&gt;2sum[a]k[i,j]&gt;2*sum[a],则i比j优,k比j优
      不论如何,j都无法成为最佳决策点,所以可以排除j
      于是,所有的决策点满足一个下凸包性质
    • 接下来看看如何找最优解。 设k&lt;j&lt;ik&lt;j&lt;i
      由于我们排除了k[i,j]&lt;k[j,k]k[i,j]&lt;k[j,k]的情况,所以整个有效点集呈现一种下凸性质,即k[i,j]&gt;k[j,k]k[i,j]&gt;k[j,k]
      这样,从左到右,斜率之间就是单调递增的了。当我们的最优解取得在j点的时候,那么k点不可能再取得比j点更优的解了,于是k点也可以排除。换句话说,j点之前的点全部不可能再比j点更优了,可以全部从解集中排除。
      在这里插入图片描述
    • 于是对于这题我们对于斜率优化做法可以总结如下:
      1.用一个单调队列来维护解集。
      2.假设队列中从头到尾已经有元素a b c。那么当d要入队的时候,我们维护队列的下凸性质,即如果k[d,c]&lt;=k[c,b]k[d,c]&lt;=k[c,b],那么就将c点删除。直到找到k[d,x]&gt;k[x,y]k[d,x]&gt;k[x,y]为止,并将d点加入在该位置中。
      3.找最佳决策点时,设当前求解状态为i,从队头开始,如果已有元素a b c,当i点要求解时,如果k[b,a]&lt;=2sum[i]k[b,a]&lt;=2*sum[i],那么说明b点比a点更优,a点可以排除,于是a出队,直到第一次遇到k[j,j1]&gt;2sum[i]k[j,j-1]&gt;2*sum[i],此时j-1即为最佳决策点。
      在这里插入图片描述
      参考代码:
    #include <bits/stdc++.h>
    using namespace std;
    const int MAXN = 500005;
    int n, m, s, t, dq[MAXN];
    int sum[MAXN], f[MAXN];
    
    inline int Getup(int i, int j) { return f[i] + sum[i]*sum[i] - f[j] - sum[j]*sum[j]; } //Yi-Yj
    inline int Getdown(int i, int j) { return sum[i] - sum[j]; } //Xi-Xj
    
    int main ()
    {
    	int x;
    	while(scanf("%d%d", &n, &m) == 2)
    	{
    		for(int i = 1; i <= n; i++) scanf("%d", &x), sum[i] = sum[i-1] + x;
    		f[0] = 0; s = t = 0; dq[t++] = 0;
    		for(int i = 1; i <= n; i++)
    		{
    			while(t-s > 1 && Getup(dq[s+1], dq[s])  <= sum[i] * 2 * Getdown(dq[s+1], dq[s])) s++;
    			f[i] = f[dq[s]] + (sum[i] - sum[dq[s]]) * (sum[i] - sum[dq[s]]) + m;
    			while(t-s > 1 && Getup(i, dq[t-1]) * Getdown(dq[t-1], dq[t-2]) <= Getup(dq[t-1], dq[t-2]) * Getdown(i, dq[t-1])) t--;
    			dq[t++] = i;
    		}
    		printf("%d
    ", f[n]);
    	}
    }
    
  • 相关阅读:
    ASP.Net GBK 解码
    windows默认共享的打开和关闭?
    NET导出Excel遇到的80070005错误的解决方法
    JavaScript(一)
    博客开通
    java知识学习05-数据类型、变量、标识符、类型转换
    java知识学习04-注释、关键字、常量
    java知识学习03-第一个程序
    【转】Git 冲突:Your local changes would be overwritten by merge. Commit, stash or revert them to proceed.
    idea如何在Git上将分支代码合并到主分支
  • 原文地址:https://www.cnblogs.com/Orz-IE/p/12039478.html
Copyright © 2020-2023  润新知