[APIO2010]特别行动队——[斜率优化DP]

本文探讨了如何通过动态规划和斜率优化技巧,解决部队士兵编队问题,以实现修正后战斗力总和最大化。文章提供了详细的算法分析及C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【题目描述】

你有一支由 n 名预备役士兵组成的部队,士兵从 1 到 n 编号,要将他们拆分 成若干特别行动队调入战场。出于默契的考虑,同一支特别行动队中队员的编号 应该连续,即为形如 ( i , i + 1 , . . . , i + k ) (i, i + 1, ..., i + k) (i,i+1,...,i+k)的序列。 编号为 i 的士兵的初始战斗力为 xi ,一支特别行动队的初始战斗力 x 为队内 士兵初始战斗力之和,
x i + x i + 1 + . . . + x i + k x_i +x_{i+1} + ... + x_{i+k} xi+xi+1+...+xi+k​ 。

通过长期的观察,你总结出一支特别行动队的初始战斗力 x 将按如下经验公 式修正为 x ′ : x ′ = a x 2 + b x + c x':x'= ax^2+bx+c x:x=ax2+bx+c,其中 a, b, c 是已知的系数(a < 0)。 作为部队统帅,现在你要为这支部队进行编队,使得所有特别行动队修正后 战斗力之和最大。试求出这个最大和。

例如,你有 4 名士兵, x 1 = 2 , x 2 = 2 , x 3 = 3 , x 4 = 4 x_1 = 2, x_2 = 2, x_3 = 3, x_4 = 4 x1=2,x2=2,x3=3,x4=4。经验公式中的参数为 a = – 1 , b = 10 , c = – 20 a = –1, b = 10, c = –20 a=1,b=10,c=20。此时,最佳方案是将士兵组成 3 个特别行动队:第一队包含士兵 1 和士兵 2,第二队包含士兵 3,第三队包含士兵 4。特别行动队的初始战斗力分 别为 4, 3, 4,修正后的战斗力分别为 4, 1, 4。修正后的战斗力和为 9,没有其它 方案能使修正后的战斗力和更大。

【输入格式】

输入由三行组成。第一行包含一个整数 n,表示士兵的总数。第二行包含三 个整数 a, b, c,经验公式中各项的系数。第三行包含 n 个用空格分隔的整数 x 1 , x 2 , … , x n x_1, x_2, …, x_n x1,x2,,xn,分别表示编号为 1 , 2 , … , n 1, 2, …, n 1,2,,n 的士兵的初始战斗力。

【输出格式】

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

S a m p l e    I n p u t Sample~~Input Sample  Input

4
-1 10 -20
2 2 3 4

S a m p l e    O u t p u t Sample~~Output Sample  Output

9

【题意分析】

正常的OI选手都可以推出的暴力dp:

d p i = max ⁡ 1 ≤ j < i { d p j + a ( s i − s j ) 2 + b ( s i − s j ) + c } dp_i=\max_{1\leq j < i}{\{dp_j+a(s_i-s_j)^2+b(s_i-s_j)+c \}} dpi=1j<imax{dpj+a(sisj)2+b(sisj)+c}

其中s[i]表示前缀和。

那么显然只能拿到20pts,这个式子具有很明显的斜率优化特征。

根据斜率优化套路假设 x x x这个决策比 y y y更优。(假设 x > y x>y x>y并且下面的 s l o p e ( x , y ) slope(x,y) slope(x,y)中x也比y大)

令j分别等于x和y,暴展得到:

d p x + a ( s i − s x ) 2 + b ( s i − s x ) + c > d p y + a ( s i − s y ) 2 + b ( s i − s y ) + c dp_x+a(s_i-s_x)^2+b(s_i-s_x)+c>dp_y+a(s_i-s_y)^2+b(s_i-s_y)+c dpx+a(sisx)2+b(sisx)+c>dpy+a(sisy)2+b(sisy)+c

⇒ ( d p x + a s x ) 2 − ( d p y + a s y ) 2 s x − s y > 2 a s i + b \Rightarrow \frac{(dp_x+as_x)^2-(dp_y+as_y)^2}{s_x-s_y}>2as_i+b sxsy(dpx+asx)2(dpy+asy)2>2asi+b

这样根据 a < 0 a<0 a<0这个条件,不等式右侧单调递减,两边同乘-1

⇒ − ( d p x + a s x ) 2 − ( d p y + a s y ) 2 s x − s y < − 2 a s i − b \Rightarrow - \frac{(dp_x+as_x)^2-(dp_y+as_y)^2}{s_x-s_y}<-2as_i-b sxsy(dpx+asx)2(dpy+asy)2<2asib

这样成功地可以进行 s l o p e slope slope维护一个下凸包

就到这里吧

Code:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cstring>
#include <cmath>
#include <algorithm>
#define int long long
#define MAXN 1000010
#define db double
using namespace std;

int sum[MAXN], dp[MAXN], q[MAXN], n, a, b, c;

inline int read () {
	register int s = 0, w = 1;
	register char ch = getchar ();
	while (! isdigit (ch)) {if (ch == '-') w = -1; ch = getchar ();}
	while (isdigit (ch)) {s = (s << 3) + (s << 1) + (ch ^ 48); ch = getchar ();}
	return s * w;
}

inline int sqr (int x) {return x * x;}
inline int f (int x) {return a * sqr (x) + b * x + c;}
inline int g (int x) {return dp[x] + a * sqr (sum[x]);}
inline db slope (int x, int y) {return -1.0 * (g (x) - g (y)) / (sum[x] - sum[y]);}

signed main () {
	n = read (), a = read (), b = read (), c = read ();
	for (register int i = 1; i <= n; i++) sum[i] = sum[i - 1] + read ();
	int h = 0, t = 0;
	for (register int i = 1; i <= n; i++) {
		while (h < t && slope (q[h + 1], q[h]) < - (2 * a * sum[i] + b)) h++;
		dp[i] = dp[q[h]] + f (sum[i] - sum[q[h]]);
		while (h < t && slope (q[t], q[t - 1]) > slope (i, q[t])) t--;
		q[++t] = i;
	}
	return printf ("%lld\n", dp[n]), 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值