斜率优化

来源:互联网 发布:在数据库中存储的是 编辑:程序博客网 时间:2024/04/26 10:50
bzoj1911 && APIO2010 特别行动队

题目描述

你有一支由n名预备役士兵组成的部队,士兵从1n编号,

要将他们拆分成若干特别行动队调入战场。

出于默契的考虑,同一支特别行动队中队员的编号应该连续,

即为形如(i, i + 1, , i + k)的序列。

编号为i的士兵的初始战斗力为xi ,一支特别行动队的初始战斗力x为队内士兵初始战斗力之和,

x = xi + xi+1 +  + xi+k

通过长期的观察,你总结出一支特别行动队的初始战斗力x将按如下经验公式修正为

x'x' = ax^2 + bx + c,其中a, b, c是已知的系数(a < 0)。

作为部队统帅,现在你要为这支部队进行编队,使得所有特别行动队修正后战斗力之和最大。

试求出这个最大和。例如,你有4名士兵,x1 = 2, x2 = 2, x3 = 3, x4 = 4

经验公式中的参数为a = 1, b =10, c = 20

此时,最佳方案是将士兵组成3个特别行动队:

第一队包含士兵1和士兵2,第二队包含士兵3,第三队包含士兵4

特别行动队的初始战斗力分别为4, 3, 4,修正后的战斗力分别为4, 1, 4

修正后的战斗力和为9,没有其它方案能使修正后的战斗力和更大。

 

输入格式

输入由三行组成。第一行包含一个整数n,表示士兵的总数。

第二行包含三个整数a, b, c,经验公式中各项的系数。

第三行包含n个用空格分隔的整数x1, x2, , xn

分别表示编号为1, 2, , n的士兵的初始战斗力。

【数据范围】
20%
的数据中, 100050%的数据中, 10,000

100%的数据中, n  1,000,000,– a  1|b|  10,000,000

|c|  10,000,000 xi  100

 

输出格式

输出一个整数,表示所有特别行动队修正后战斗力之和的最大值。

 

样例输入

4

-1 10 -20

2 2 3 4

样例输出

9

这是一个斜率优化的题

朴素的可以想到有

dp[i] = dp[j] + a*SQR(s[i]-s[j])+b*(s[i]-s[j])+c

对于dp[i]有两个决策j,k,且k更优,则有

dp[k]-dp[j]-2*s[i]*s[k]+2*s[i]*s[j]+a*s[k]^2-a*s[j]^2-b*s[k]+b*s[j]>0

整理得

(dp[k]+a*s[k]^2-b*s[k]) – (dp[j]+a*s[j]^2-b*s[j]) > 2*a*s[i]*(s[k]-s[j])

设函数F(i) = dp[i]+a*s[i]^2-b*s[i]

X(i,j)=(F(k) – F(j)) / (s[k] – s[j])

这就是斜率式了,后面就是维护一个单调队列就好了

第一次写(抄)斜率优化,看了半天定义,以前寥寥草草知道一点队尾的操作,不知道队头还有操作

下面抄一段证明。。。。

现在从左到右,还是设k<j<i,如果g[i,j]<g[j,k],那么j点便永远不可能成为最优解,可以直接将它踢出我们的最优解集。为什么呢?

我们假设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点。

排除多余的点,这便是一种优化!

 

接下来看看如何找最优解。

设k<j<i。

由于我们排除了g[i,j]<g[j,k]的情况,所以整个有效点集呈现一种上凸性质,即k j的斜率要大于j i的斜率。

这样,从左到右,斜率之间就是单调递减的了。当我们的最优解取得在j点的时候,那么k点不可能再取得比j点更优的解了,于是k点也可以排除。换句话说,j点之前的点全部不可能再比j点更优了,可以全部从解集中排除。

 

于是对于这题我们对于斜率优化做法可以总结如下:

1,用一个单调队列来维护解集。

2,假设队列中从头到尾已经有元素a b c。那么当d要入队的时候,我们维护队列的上凸性质,即如果g[d,c]<g[c,b],那么就将c点删除。直到找到g[d,x]>=g[x,y]为止,并将d点加入在该位置中。

3,求解时候,从队头开始,如果已有元素a b c,当i点要求解时,如果g[b,a]<sum[i],那么说明b点比a点更优,a点可以排除,于是a出队。最后dp[i]=getDp(q[head])。


#include<cstdio>#include<cstring>#include<algorithm>#include<iostream>using namespace std;#define SQR(x) (x) * (x)typedef long long LL;LL n, a, b, c, s[1000010], q[1000010], data[1000010];inline double F(int k){return data[k] + a * SQR(s[k]) - b * s[k];}inline double X(int k, int j){return (F(k) - F(j)) / (s[k] - s[j]);}int main(){       int i;           cin>>n>>a>>b>>c;       s[0] = 0;       for(i = 1; i <= n; i++){cin>>s[i]; s[i] += s[i - 1];}       int head = 0, tail = 0; q[0] = data[0] = 0;//习惯于这么弄初始状态。       for(i = 1; i <= n; i++)       {              int p = 2 * a * s[i];              while(head < tail && X(q[head], q[head + 1]) > p) ++head;              int xx = q[head];              data[i] = data[xx] + a * SQR(s[i] - s[xx]) + b * (s[i] - s[xx]) + c;              while(head < tail && X(q[tail], i) > X(q[tail - 1], q[tail])) --tail;              q[++tail] = i;       }       cout<<data[n];return 0;}

0 0
原创粉丝点击