题目大意

给定一个长度为\(n(n \leqslant 500000)\)的数列,将其分割为连续的若干份,使得 $ \sum ((\sum_{i=j}^kC_i) +M) $ 最小。其中\(C_i\)为序列中的项的值,\(M\)为常数。$ j,k $ 表示在原序列中连续的某一段的起始位置和结束位置。

解题思路

考虑到\(n\)的范围巨大,肯定不能用\(O(n^2)\)的暴力DP,而贪心又显然有问题,所以我们只能尝试对DP优化。

我们设\(f[i]\)为前\(i\)项作为子问题的解,\(sum[i]\)为前\(i\)项的前缀和。那么,若从\(i\)转移到\(k\)优于从\(j\)转移到\(k\)(不妨令\(i > j\))就有:

\[f[i]+M+(sum[k]-sum[i])^2 < f[j]+M+(sum[k]-sum[j])^2
\]

化简,得

\[\frac{f[i]-f[j]+sum[i]^2-sum[j]^2}{2sum[i]-2sum[j]}<sum[k]
\]

到这里,做法就显然了,就是DP斜率优化。

接下来就在这道题的基础上大致分析一下什么是斜率优化。


我们不妨令\(Y[i]=f[i]-sum[i]^2,X[i]=2sum[i]\)。那么上面不等式的左边就变为了\(\frac{Y[i]-Y[j]}{X[i]-X[j]}\)。这个东西是不是很像斜率呢?\(X,Y\)可以看成点。我们不妨设现在从左至右有\(3\)个点\(i,j,k\),\(i,j\)斜率为\(l_1\),\(j,k\)斜率为\(l_2\)。接下来我们考虑\(l_1,l_2\)。

当\(l_2 \leqslant l_1\)时,若\(sum[k] \leqslant l_2 \leqslant l_1\),那么最优值不是\(j,k\);若\(l_2 < sum[k] \leqslant l_1\),那么\(k\)比\(j\)优;若\(l_2 \leqslant l_1 < sum[k]\),那么\(k\)在\(i,j,k\)中最优。所以不论如何,\(j\)都不会成为当前最优方案,我们不妨删掉\(j\)。

当\(l_1 < l2\)时,若\(sum[k] \leqslant l_1 < l_2\),那么最优值可能是\(i\);若\(l_1 < sum[k] \leqslant l_2\),那么\(j\)在\(i,j,k\)中最优;若\(l_1 < l_2 < sum[k]\),那么最优值可能为\(k\)。

进过如上分析,我们发现,我们只需要保留在图上逐个连线后样子为下凸的一些点。同时我们又发现,若从点\(i\)转移为当前最优,那么在图上看来这个点应该与斜率为\(sum[k]\)的直线“相切”。所以我们转移的时候只需要找在保留的点中,向前斜率小于\(sum[k]\),向后斜率大于\(sum[k]\)的点就可以了。

最后,这里sum[k]单调不减,所以找当前最优的转移可以优化;若遇到\(sum[k]\)不单调的情况,二分查找即可。

tip:推式子的时候不能忽略取等的情况。我就是因为\(Greater\)函数中漏了取等的情况,听取WA声一片……

补:后来发现实际上是可能不严格递增,导致判断的时候某个结果为\(0\)

参考程序

#include <bits/stdc++.h>
#define LL long long
using namespace std; LL N, M, a[ 500010 ], Sum[ 500010 ], F[ 500010 ];
LL L, R, Queue[ 500010 ]; LL sqr( LL x ) { return x * x; } bool Less( LL j, LL i, LL t ) {
return F[ i ] - F[ j ] + sqr( Sum[ i ] ) - sqr( Sum[ j ] ) < 2 * Sum[ t ] * ( Sum[ i ] - Sum[ j ] );
} bool Greater( LL k, LL j, LL i ) {
LL X2 = 2 * ( Sum[ i ] - Sum[ j ] );
LL Y2 = F[ i ] - F[ j ] + sqr( Sum[ i ] ) - sqr( Sum[ j ] );
LL X1 = 2 * ( Sum[ j ] - Sum[ k ] );
LL Y1 = F[ j ] - F[ k ] + sqr( Sum[ j ] ) - sqr( Sum[ k ] );
return X1 * Y2 <= X2 * Y1;
} int main() {
while( scanf( "%lld%lld", &N, &M ) == 2 ) {
memset( a, 0, sizeof( a ) );
memset( Sum, 0, sizeof( Sum ) );
memset( F, 0, sizeof( F ) );
memset( Queue, 0, sizeof( Queue ) );
L = R = 0;
for( LL i = 1; i <= N; ++i ) scanf( "%lld", &a[ i ] );
for( LL i = 1; i <= N; ++i ) Sum[ i ] = Sum[ i - 1 ] + a[ i ];
R = 1; Queue[ 0 ] = 0;
for( LL i = 1; i <= N; ++i ) {
while( L + 1 < R && Less( Queue[ L ], Queue[ L + 1 ], i ) )
++L;
F[ i ] = F[ Queue[ L ] ] + M + sqr( Sum[ i ] - Sum[ Queue[ L ] ] );
while( L + 1 < R && Greater( Queue[ R - 2 ], Queue[ R - 1 ], i ) )
--R;
Queue[ R++ ] = i;
}
printf( "%lld\n", F[ N ] );
}
return 0;
}

HDU 3507 [Print Article]DP斜率优化的更多相关文章

  1. HDU 3507 Print Article(斜率优化DP)

    题目链接 题意 : 一篇文章有n个单词,如果每行打印k个单词,那这行的花费是,问你怎么安排能够得到最小花费,输出最小花费. 思路 : 一开始想的简单了以为是背包,后来才知道是斜率优化DP,然后看了网上 ...

  2. HDU 3507 Print Article(斜率优化)

    显然的斜率优化模型 但是单调队列维护斜率单调性的时候出现了莫名的锅orz 代码 #include <cstdio> #include <algorithm> #include ...

  3. HDU 3507 Print Article(斜率优化推导)

    $dp$,斜率优化. 第一次做斜率优化的题目,看了一些题解,自己总结一下. 这题是说有$n$个数字,可以切成任意段,每一段的费用是这一段数字的和平方加上$M$.问最小费用是多少. 设$dp[i]$为$ ...

  4. hdu 3507 Print Article(斜率优化DP)

    题目链接:hdu 3507 Print Article 题意: 每个字有一个值,现在让你分成k段打印,每段打印需要消耗的值用那个公式计算,现在让你求最小值 题解: 设dp[i]表示前i个字符需要消耗的 ...

  5. HDU 3507 Print Article(DP+斜率优化)

     Print Article Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others) ...

  6. DP(斜率优化):HDU 3507 Print Article

    Print Article Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others)To ...

  7. HDU 3507 - Print Article - [斜率DP]

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3507 Zero has an old printer that doesn't work well s ...

  8. HDU 3507 Print Article 斜率优化

    Print Article Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others)To ...

  9. 斜率优化板题 HDU 3507 Print Article

    题目大意:输出N个数字a[N],输出的时候可以连续的输出,每连续输出一串,它的费用是 "这串数字和的平方加上一个常数M".n<=500000 我们设dp[i]表示输出到i的时 ...

随机推荐

  1. %300为0的个数(牛客第四场)-- number

    题意: 给你一串数,问你如题. 思路: 我不是这样的作法,从后往前,先取00,再算%3==0的个数,往前推的时候有递推关系: #define IOS ios_base::sync_with_stdio ...

  2. @RequestMapping-标准映射和Ant风格的映射

    4.@RequestMapping 如果value不以“/”开头,SpringMVC会自动添加“/” 4.1.@RequestMapping映射 4.1.1.标准URL映射 4.1.2.Ant风格的U ...

  3. 关于解决SpringDataJpa框架实体类表字段创建顺序与数据库表字段展示顺序不一致的问题

    今天在公司的项目开发中,遇到一个问题: 后端对象实体类中写入字段顺序与数据库中的存储顺序不一致. 仔细观察到数据库中的表字段的排序方式是按照拼音字母的顺序abcdef......来存储的 而我的实体类 ...

  4. LVS Director端服务启动脚本

    #!/bin/bash # 手动安装lpvs前端管理工具 # chkconfig: - # # lvs启动脚本:director # lvs模式类型:nat.dr.ipip # lvs代理协议:tcp ...

  5. shelve:极其强大的序列化模块

    介绍 数据持久化,就是把数据从内存刷到磁盘上.但是要保证在读取的时候还能恢复到原来的状态.像pickle和json之类的持久化模块基本上无需介绍了,这里介绍两个其他很少用但是功能很强大的模块. dbm ...

  6. linux 指定ftp用户 特定目录及权限

    Linux添加FTP用户并设置权限   在linux中添加ftp用户,并设置相应的权限,操作步骤如下:  1.环境:ftp为vsftp.被限制用户名为test.被限制路径为/home/test 2.建 ...

  7. centos7 Keepalived + Haproxy + MySQL pxc5.6

    拓扑图 应用通过 VIP 连接到 Haproxy,Haproxy 通过http代理分发请求到后端 3 台 MySQL pxc. Keepalived 可以有效防止 Haproxy 单点故障. MySQ ...

  8. top 1 1

    这个是今天刚发现的,以前都是top 10 这样的用法,今天看到这个用法竟然懵逼了,所以到网站查了一下, 百度上的结果: 这个是SQLSERVER选择表中符合条件的前N行记录的语句.不过,TOP后边跟的 ...

  9. 转(static final 和final的区别)

    学习java的时候常常会被修饰符搞糊涂,这里总结下static final和final的区别. 1.static 强调只有一份,final 说明是一个常量,final定义的基本类型的值是不可改变的,但 ...

  10. Summer training round2 #9(Training28)

    A:签到题 C:模拟搜索题 #include <bits/stdc++.h> #include <cstring> #include <iostream> #inc ...