Hdu3057(划分类DP+基础斜率优化)

本文深入探讨了利用动态规划(DP)和单调队列优化解决复杂问题的方法,特别关注了如何通过减少重复枚举来提升算法效率。文章详细解释了状态转移方程,并通过实例展示了如何通过数学归纳法优化决策过程,进而实现对大规模数据集的有效处理。同时,阐述了在具体场景下如何通过单调队列维护最优解,从而在保持问题规模的同时,达到高效的计算结果。

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

题目大意

a[1..n]a[1..n]a[1..n]划分成若干个区间,每段区间[i,j][i , j][i,j]的代价为(c[i]+c[i+1]+c[i+2]+…+c[j])2+m(c[i] + c[i + 1] + c[i + 2] + … + c[j]) ^ 2 + m(c[i]+c[i+1]+c[i+2]++c[j])2+m。其中mmm为常数。
求最小的总代价和。
a[i]>0a[i] > 0a[i]>0
n≥5∗104n ≥ 5 * 10 ^ 4n5104


思路

首先,这是个很明显的划分类DPDPDP
直接上状态:f[i]f[i]f[i]表示前i个数的最小代价。
易得f[i]=minf[j]+(s[i]−s[j])2+mf[i] = min{f[j] + (s[i] - s[j]) ^ 2 + m}f[i]=minf[j]+(s[i]s[j])2+m
初值:f[0]=0f[0]=0f[0]=0
终值:f[n]f[n]f[n]


-----------华------------丽------------丽------------的------------分------------割------------线-----------


然后,我们发现,这是个非常经典的1D/1D1D/1D1D/1D动态规划
显然是无法通过5∗1045*10^45104级别的数据。
考虑优化,对于一道一维状态的DPDPDP题,优化方向只有一个——转移。
如何减少重复或不必要的枚举呢?
1&lt;j1&lt;j2&lt;i1 &lt; j_1 &lt; j_2 &lt; i1<j1<j2<i
则对于iii的决策,j2j_2j2j1j_1j1优等价于满足
f[j2]+(s[i]−s[j2])2+m&lt;f[j1]+(s[i]−s[j1])2+mf[j2] + (s[i] - s[j2]) ^ 2 + m &lt; f[j1] + (s[i] - s[j1]) ^ 2 + mf[j2]+(s[i]s[j2])2+m<f[j1]+(s[i]s[j1])2+m
简单的打开并整理过后就得到了
(f[j2]−f[j1])+(s[j2]2−s[j1]2)(f[j_2]-f[j_1]) + (s[j_2] ^ 2 - s[j_1] ^ 2)(f[j2]f[j1])+(s[j2]2s[j1]2)
--------------------------------------------------- &lt;s[i]&lt; s[i]<s[i]
(s[j2]−s[j1])∗2(s[j2] - s[j1]) * 2(s[j2]s[j1])2
设不等式左边=g(j1,j2)=g(j_1 , j_2)=g(j1,j2)
先考虑用代数
g(j1,j2)&gt;g(j2,j3)g(j_1 , j_2) &gt; g(j_2 , j_3)g(j1,j2)>g(j2,j3)则由上面的推论可以知道对于i的决策,j1j_1j1j2j_2j2优,j3j_3j3j2j_2j2优,即j2j_2j2永远不可能成为最优的转移
又因为a[i]&gt;0a[i] &gt; 0a[i]>0,即s[i]&lt;s[i+1]&lt;…&lt;s[n]s[i] &lt; s[i + 1] &lt; … &lt; s[n]s[i]<s[i+1]<<s[n]
j2j_2j2永远不可能成为今后的最优转移
因此,我们可以通过不转移形如g(j1,j2)&gt;g(j2,j3)g(j_1,j_2)&gt;g(j_2,j_3)g(j1,j2)>g(j2,j3)j2j_2j2
换言之,我们要转移的应该是满足g(j1,j2)≤g(j2,j3)≤…g(j_1,j_2)≤g(j_2,j_3)≤ …g(j1,j2)g(j2,j3)
用一个单调队列即可完成维护,每次在队首取值即可
队列中存的是决策点的下标,以队列中相邻两元素构成的g()g()g()为关键字单调递增
g(j1,j2)≤g(j2,j3)g(j_1,j_2)≤g(j_2,j_3)g(j1,j2)g(j2,j3)为例
①当s[i]≤g(j1,j2)≤g(j2,j3)s[i]≤g(j_1,j_2)≤g(j_2,j_3)s[i]g(j1,j2)g(j2,j3)时,j1j_1j1j2j_2j2优,j2j_2j2j3j_3j3优 取j1j_1j1
②当g(j1,j2)≤s[i]≤g(j2,j3)g(j_1,j_2)≤s[i]≤g(j_2,j_3)g(j1,j2)s[i]g(j2,j3)时,j2j_2j2j1j_1j1优,j2j_2j2j3j_3j3优 弹出j1j_1j1j2j_2j2
③当g(j1,j2)≤g(j2,j3)≤s[i]g(j_1,j_2)≤g(j_2,j_3)≤s[i]g(j1,j2)g(j2,j3)s[i]时,j3j_3j3j2j_2j2优,j2j_2j2j1j_1j1优 弹出j2j_2j2j3j_3j3


然后再考虑用较为简便的“数形结合”
草图
发现这恰好与上面的代数一一对应,将g看成一条直线,将s[i]也看成一条直线
分别讨论当s[i]s[i]s[i]的斜率①x&lt;j1j2x &lt; j_1j_2x<j1j2j1j2&lt;x&lt;j2j3j_1j_2 &lt; x &lt; j_2j_3j1j2<x<j2j3x&gt;j2j3x &gt; j_2j_3x>j2j3
最终可得与上文相同结论

代码

#include<cstdio>
using namespace std;
const int maxn = 500005;
long long s[maxn] , f[maxn];
int a[maxn] , q[maxn];
inline int read()
{
   char ch = getchar();
   while(ch < '0' || ch > '9') ch = getchar();
   int x = 0;
   while(ch >= '0' && ch <= '9') x = x * 10 + ch - 48 , ch = getchar();
   return x;
}
long long sqr(long long x)
{
   return x * x;
}
bool cmp1(int j2 , int j1 , int k)
{
   return (f[j2] - f[j1] + sqr(s[j2]) - sqr(s[j1])) <= k * 2 * (s[j2] - s[j1]);
}
bool cmp2(int j3 , int j2 , int j1)
{
   return (f[j3] - f[j2] + sqr(s[j3]) - sqr(s[j2])) * (s[j2] - s[j1]) <= (f[j2] - f[j1] + sqr(s[j2]) - sqr(s[j1])) * (s[j3] - s[j2]);
}
int main()
{
   int n = read() , m = read();
   s[0] = 0;
   for(int i = 1;i <= n;i++) a[i] = read() , s[i] = s[i - 1] + a[i];
   int head = 1 , tail = 1;
   q[1] = 0;
   f[0] = 0;
   for(int i = 1;i <= n;i++)
   {
   	while(head < tail && cmp1(q[head + 1] , q[head] , s[i])) head++;
   	f[i] = f[q[head]] + (s[i] - s[q[head]]) * (s[i] - s[q[head]]) + m;
   	while(head < tail && cmp2(i , q[tail] , q[tail - 1])) tail--;
   	q[++tail] = i;
   }
   printf("%lld\n",f[n]);
   return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值