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

题目大意

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。其中 m m m为常数。
求最小的总代价和。
a [ i ] > 0 a[i] > 0 a[i]>0
n ≥ 5 ∗ 1 0 4 n ≥ 5 * 10 ^ 4 n5104


思路

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


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


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


然后再考虑用较为简便的“数形结合”
草图
发现这恰好与上面的代数一一对应,将g看成一条直线,将s[i]也看成一条直线
分别讨论当 s [ i ] s[i] s[i]的斜率① x &lt; j 1 j 2 x &lt; j_1j_2 x<j1j2 j 1 j 2 &lt; x &lt; j 2 j 3 j_1j_2 &lt; x &lt; j_2j_3 j1j2<x<j2j3 x &gt; j 2 j 3 x &gt; j_2j_3 x>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、付费专栏及课程。

余额充值