【JZOJ2318】【BZOJ1911】【APIO2010】特别行动队

本文介绍了一种利用斜率优化技巧改进动态规划算法的方法,适用于特定形式的转移方程。通过对实例问题的分析,详细展示了如何推导斜率条件,并使用单调队列维护最优解的过程。

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

description

你有一支由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,没有其它方案能使修正后的战斗力和更大。


analysis

  • 达成成就一天两斜率优化

  • f [ i ] f[i] f[i]表示到第 i i i位的最小值,那么 O ( n 2 ) O(n^2) O(n2) D P DP DP方程很好写

f [ i ] = m i n ( f [ j ] + a ( p r e [ i ] − p r e [ j ] ) 2 + b ( p r e [ i ] − p r e [ j ] ) + c ) f[i]=min(f[j]+a(pre[i]-pre[j])^2+b(pre[i]-pre[j])+c) f[i]=min(f[j]+a(pre[i]pre[j])2+b(pre[i]pre[j])+c)

  • 对于 k &gt; j k&gt;j k>j k k k j j j优则有

f [ k ] + a ( p r e [ i ] − p r e [ k ] ) 2 + b ( p r e [ i ] − p r e [ k ] ) + c ≥ f [ j ] + a ( p r e [ i ] − p r e [ j ] ) 2 + b ( p r e [ i ] − p r e [ j ] ) + c f[k]+a(pre[i]-pre[k])^2+b(pre[i]-pre[k])+c≥f[j]+a(pre[i]-pre[j])^2+b(pre[i]-pre[j])+c f[k]+a(pre[i]pre[k])2+b(pre[i]pre[k])+cf[j]+a(pre[i]pre[j])2+b(pre[i]pre[j])+c

  • 拆项移项得

f [ k ] − f [ j ] ≥ a ( p r e [ i ] − p r e [ j ] ) 2 − a ( p r e [ i ] − p r e [ k ] ) 2 + b ( p r e [ i ] − p r e [ j ] ) − b ( p r e [ i ] − p r e [ k ] ) f[k]-f[j]≥a(pre[i]-pre[j])^2-a(pre[i]-pre[k])^2+b(pre[i]-pre[j])-b(pre[i]-pre[k]) f[k]f[j]a(pre[i]pre[j])2a(pre[i]pre[k])2+b(pre[i]pre[j])b(pre[i]pre[k])

  • 再移

f [ k ] − f [ j ] ≥ a ( p r e [ i ] 2 − 2 p r e [ i ] p r e [ j ] + p r e [ j ] 2 ) − a ( p r e [ i ] 2 − 2 p r e [ i ] p r e [ j ] + p r e [ k ] 2 ) + b ( p r e [ k ] − p r e [ j ] ) f[k]-f[j]≥a(pre[i]^2-2pre[i]pre[j]+pre[j]^2)-a(pre[i]^2-2pre[i]pre[j]+pre[k]^2)+b(pre[k]-pre[j]) f[k]f[j]a(pre[i]22pre[i]pre[j]+pre[j]2)a(pre[i]22pre[i]pre[j]+pre[k]2)+b(pre[k]pre[j])

f [ k ] + a p r e [ k ] 2 − f [ j ] − a p r e [ j ] 2 − b ( p r e [ k ] − p r e [ j ] ) ≥ a p r e [ i ] ( 2 p r e [ k ] − 2 p r e [ j ] ) f[k]+apre[k]^2-f[j]-apre[j]^2-b(pre[k]-pre[j])≥apre[i](2pre[k]-2pre[j]) f[k]+apre[k]2f[j]apre[j]2b(pre[k]pre[j])apre[i](2pre[k]2pre[j])

  • 最后

f [ k ] + a p r e [ k ] 2 − f [ j ] − a p r e [ j ] 2 − b ( p r e [ k ] − p r e [ j ] ) 2 a ( p r e [ k ] − p r e [ j ] ) ≥ p r e [ i ] {{f[k]+apre[k]^2-f[j]-apre[j]^2-b(pre[k]-pre[j])}\over{2a(pre[k]-pre[j])}}≥pre[i] 2a(pre[k]pre[j])f[k]+apre[k]2f[j]apre[j]2b(pre[k]pre[j])pre[i]

  • 然后按套路上单调队列斜率优化维护上凸壳就可以了

  • 注意先删去较劣的队头后删去较劣的队尾

  • 还有这题删劣决策点的符号很奇怪,我也不太懂

  • 你听说什么是如出一辙吗,推荐试下找不同


code

#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define MAXN 1000005
#define mod 1000000007
#define ll long long
#define reg register ll
#define fo(i,a,b) for (reg i=a;i<=b;++i)
#define fd(i,a,b) for (reg i=a;i>=b;--i)
#define O3 __attribute__((optimize("-O3")))

using namespace std;

ll a[MAXN],f[MAXN],pre[MAXN],que[MAXN];
ll n,A,B,C;

O3 inline ll read()
{
	ll x=0,f=1;char ch=getchar();
	while (ch<'0' || '9'<ch){if (ch=='-')f=-1;ch=getchar();}
	while ('0'<=ch && ch<='9')x=x*10+ch-'0',ch=getchar();
	return x*f;
}
O3 inline ll sqr(ll x)
{
	return x*x;
}
O3 inline double slope(ll x,ll y)
{
	return 1.0*(f[x]+A*sqr(pre[x])-f[y]-A*sqr(pre[y])-B*(pre[x]-pre[y]))/(2.0*A*(pre[x]-pre[y]));
}
O3 int main()
{
	//freopen("T3.in","r",stdin);
	n=read(),A=read(),B=read(),C=read();
	fo(i,1,n)pre[i]=pre[i-1]+(a[i]=read());
	ll head=1,tail=1;f[0]=que[1]=0;
	fo(i,1,n)
	{
		while (head<tail && slope(que[head],que[head+1])<=pre[i])++head;
		f[i]=f[que[head]]+A*sqr(pre[i]-pre[que[head]])+B*(pre[i]-pre[que[head]])+C;
		while (head<tail && slope(que[tail-1],que[tail])>=slope(que[tail],i))--tail;
		que[++tail]=i;
	}
	printf("%lld\n",f[n]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值