[斜率优化] 特别行动队 commando

编程入门 行业动态 更新时间:2024-10-25 19:35:09

[<a href=https://www.elefans.com/category/jswz/34/1748047.html style=斜率优化] 特别行动队 commando"/>

[斜率优化] 特别行动队 commando

特别行动队
【问题描述】
你有一支由 n 名预备役士兵组成的部队,士兵从 1 到 n 编号,要将他们拆分成若干特别行动队调入战场。出于默契的考虑,同一支特别行动队中队员的编号应该连续,即为形如(i, i + 1, …, i + k)的序列。
编号为 i 的士兵的初始战斗力为 xi ,一支特别行动队的初始战斗力 x 为队内士兵初始战斗力之和,即 x = xi + xi+1 + … + xi+k。
通过长期的观察,你总结出一支特别行动队的初始战斗力 x 将按如下经验公式修正为 x':x' = ax2 + 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 的士兵的初始战斗力。
【输出格式】
输出一个整数,表示所有特别行动队修正后战斗力之和的最大值。
【样例输入】
4
-1 10 -20
2 2 3 4
【样例输出】
9
【数据范围】
20%的数据中,n ≤ 1000;
50%的数据中,n ≤ 10,000;
100%的数据中,1 ≤ n ≤ 1,000,000,–5 ≤ a ≤ –1,|b| ≤ 10,000,000,|c| ≤10,000,000,1 ≤ xi ≤ 100。

之所以用这道题练手是因为印象中其方程特别裸:f[i] = max{f[j] + a * sum(j + 1, i) ^ 2 + b * sum(j + 1, i) + c}。

如何用所谓的斜率优化来写这道题呢?这不像二分栈一样,一看到就可以直接写,反正不会错,而是需要证明并求出斜率才可以。

如何求斜率呢?我套用一般资料上所说的方法:设决策 j > k 且 g[k] > g[j] (g 为转移的代价),则可以展开代价得到一个类似于斜率的不等式:

(yk - yj) / (xk - xj) < h[i] (y 和 x 分别为随决策变化的表达式,而h[i] 为与 k, j 无关而仅与转出有关的常数,总之就是移项然后除一下)。

如此只要不等式成立且 h[i] 随 i 单调递增,则 k 决策将永远优于 j 决策(因为这里所用的是小于号,如果当前小于那么就会一直小于)。

如何利用这个性质呢?可以用单调队列维护决策和斜率单调,并且每次剔除无用决策。同于一般的单调队列优化,每个元素出队入队最多一次,所以时间复杂度为 O(n) 。

至于编程复杂度,完虐二分栈。。。。而且常数和时间复杂度也都优很多。。。试想 100w 或 200w 的数据而时限 1s,二分栈可以跑到一两秒,而斜率优化无压力。。。

Code : 

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>#define swap(a, b, t) ({t _ = (a); (a) = (b); (b) = _;})
#define max(a, b) ({int _ = (a), __ = (b); _ > __ ? _ : __;})
#define min(a, b) ({int _ = (a), __ = (b); _ < __ ? _ : __;})#define maxn 1000005#define sqr(a) ({double _ = (a); _ * _;})int n, h, t;
int q[maxn];
double a, b, c;
double x[maxn], s[maxn], f[maxn];double slope(int i, int j)
{return (f[j] - f[i] + a * (sqr(s[j]) - sqr(s[i])) + b * (s[i] - s[j])) / (s[j] - s[i]);
}int main()
{freopen("commando.in", "r", stdin);freopen("commando.out", "w", stdout);scanf("%d%lf%lf%lf", & n, & a, & b, & c);for (int i = 1; i <= n; ++ i)scanf("%lf", & x[i]), s[i] = s[i - 1] + x[i];q[++ t] = 0;for (int i = 1; i <= n; ++ i){while (h + 1 < t && slope(q[h + 2], q[h + 1]) > 2 * a * s[i]) ++ h;f[i] = f[q[h + 1]] + a * sqr(s[i] - s[q[h + 1]]) + b * (s[i] - s[q[h + 1]]) + c;while (t - 1 > h && slope(q[t - 1], q[t]) < slope(q[t], i)) -- t; q[++ t] = i;}printf("%.0lf\n", f[n]);return 0;
}

再是二分栈的代码,瞬间长了一倍(包括头文件等),而且抛开读入慢得多:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <climits>
#include <iostream>
#include <algorithm>typedef double TYPE;
typedef long long int64;
typedef unsigned int uint;
typedef unsigned long long uint64;#define swap(a, b, t) ({t _ = (a); (a) = (b); (b) = _;})
#define MAX(a, b, t) ({t _ = (a), __ = (b); _ > __ ? _ : __;})
#define MIN(a, b, t) ({t _ = (a), __ = (b); _ < __ ? _ : __;})
#define max(a, b) ({TYPE _ = (a), __ = (b); _ > __ ? _ : __;})
#define min(a, b) ({TYPE _ = (a), __ = (b); _ < __ ? _ : __;})#define maxn 1000005
#define oo 0xFFFFFFF
#define sum(i, j) (s[j] - s[(i) - 1])
#define xxx(k) ({double _ = k; A * _ * _ + B * _ + C;})double A, B, C;
int n, head, tail;
double a[maxn], s[maxn], f[maxn];
struct que{int l, r, x;} q[maxn];void push(int l, int r, int x)
{q[++ tail].x = x;q[tail].l = l, q[tail].r = r;
}int pop_head()
{int ans = q[head + 1].x;if (++ q[head + 1].l > q[head + 1].r) ++ head;return ans;
}void pop_tail()
{if (! -- tail) return;q[tail].r = q[tail + 1].r;
}int main()
{freopen("commando.in", "r", stdin);freopen("commando.out", "w", stdout);scanf("%d%lf%lf%lf", & n, & A, & B, & C);for (int i = 1; i <= n; ++ i)scanf("%lf", & a[i]), s[i] = s[i - 1] + a[i], f[i] = - oo;push(1, n, 0);for (int i = 1; i <= n; ++ i){int j = pop_head();f[i] = f[j] + xxx(sum(j + 1, i));while (head < tail){int l = q[tail].l, r = q[tail].r, x = q[tail].x;if (f[i] + xxx(sum(i + 1, r)) < f[x] + xxx(sum(x + 1, r))) break;if (f[i] + xxx(sum(i + 1, l)) > f[x] + xxx(sum(x + 1, l))) {pop_tail(); continue;}for (int m = l + r >> 1; l < r; m = l + r >> 1)f[i] + xxx(sum(i + 1, m)) > f[x] + xxx(sum(x + 1, m)) ? r = m : l = m + 1;q[tail].r = l - 1, push(l, n, i); break;}if (head == tail) push(i + 1, n, i);}printf("%.0lf\n", f[n]);return 0;
}



更多推荐

[斜率优化] 特别行动队 commando

本文发布于:2023-06-22 02:38:09,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/826497.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:斜率   commando

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!