HDU 3507 Print Article(斜率优化推导)
$dp$,斜率优化。
第一次做斜率优化的题目,看了一些题解,自己总结一下。
这题是说有$n$个数字,可以切成任意段,每一段的费用是这一段数字的和平方加上$M$。问最小费用是多少。
设$dp[i]$为$1$至$i$分段的最小费用,那么$dp[i]=min(dp[j]+M+(sum[i]-sum[j])^2)$。
直接计算的话,时间复杂度是$O(n^2)$,但是这题$n$有$500000$,稳稳的超时。因此,有人想出了斜率优化......
假设有三个位置$a$,$b$,$c$,$a<b<c$。
用$dp[a]$推$dp[c]$:$dp[c]=dp[a]+M+(sum[c]-sum[a])^2$
用$dp[b]$推$dp[c]$:$dp[c]=dp[b]+M+(sum[c]-sum[b])^2$
如果用$b$推$c$比用$a$推$c$要优,即$dp[a]+M+(sum[c]-sum[a])^2>dp[b]+M+(sum[c]-sum[b])^2$。
那么,化简可以得到$[(dp[b]+sum[b]^2)-(dp[a]+sum[a]^2)]/(2*sum[b]-2*sum[a])<sum[c]$。
设$yb=dp[b]+sum[b]^2$,$ya=dp[a]+sum[a]^2$,$xb=2*sum[b]$,$xa=2*sum[a]$。
那么:如果$a<b<c$,且$k[b,a]=(yb-ya)/(xb-xa)<sum[c]$,就可以说 用$b$推$c$比用$a$推$c$要优。
接下来,看一种情况:
设有四个位置:$a<b<c<d$。分别用$a,b,c$位置去推$d$位置。
如果$k[c,b]<k[b,a]$,那么$d$位置的$dp$值绝对不可能从$b$位置推过来。
简单证明如下:
如果$k[b,a]<sum[d]$,也就是说$b$比$a$优,但是$c$比$b$优,所以$b$没用。
如果$k[b,a]>=sum[d]$,也就是说$a$比$b$优,$b$也没用了。
所以在$a<b<c$且$k[c,b]<k[b,a]$的情况下,$b$位置就一定是没用的。
利用上述加红加粗的两个性质,就可以优化这个问题了。需要用一个队列来维护目前还可能往后递推的位置。
计算$dp[i]$的时候,因为队列中相邻两点$x,y$形成的线段的斜率是不断增加的,需要找到第一个不满足$k[y,x]<sum[i]$的$x$,并且$x$之前所有元素均可以从队列中删去,因为$sum[i+1]>sum[i]$,在推$dp[i]$的时候已经用不到那些元素了(被删的元素满足第一条红色性质,都没有$x$优),在推$dp[i+1]$的时候就更加用不到了。
计算得到$dp[i]$之后,可以利用第二条红色性质更新队列,为计算$dp[i+1]$做准备。每次取出队列尾部两个元素和$i$去计算,看是否满足性质$2$,满足的话,队尾就被删了;否则更新结束。
然后计算$dp[i+1]$.......
这样一优化,每个点最多被入队一次,最多出队一次,时间复杂度降到了$O(n)$。
第一次学习斜率优化,感到有一些神奇巧妙,但好像有点难啊......
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<ctime>
#include<iostream>
using namespace std;
typedef long long LL;
const double pi=acos(-1.0),eps=1e-;
void File()
{
freopen("D:\\in.txt","r",stdin);
freopen("D:\\out.txt","w",stdout);
}
template <class T>
inline void read(T &x)
{
char c = getchar();
x = ;
while(!isdigit(c)) c = getchar();
while(isdigit(c))
{
x = x * + c - '';
c = getchar();
}
} int n,m;
int q[],dp[], sum[];
int f1,f2; bool check(int a,int b,int c)
{
//ba斜率大于等于sum[c] return 1; 否则 return 0;
if(dp[b]+sum[b]*sum[b]-dp[a]-sum[a]*sum[a]<sum[c]**(sum[b]-sum[a])) return ;
return ;
} bool check2(int a,int b,int c)
{
//bc斜率大于等于ab 则不删b,否则就删b
//cb斜率大于等于ba return 1; 否则 return 0
if((dp[c]+sum[c]*sum[c]-dp[b]-sum[b]*sum[b])**(sum[b]-sum[a])<(dp[b]+sum[b]*sum[b]-dp[a]-sum[a]*sum[a])**(sum[c]-sum[b])) return ;
return ;
} int main()
{
while(~scanf("%d%d",&n,&m))
{
sum[]=; f1=;f2=-; int cnt=; for(int i=;i<=n;i++)
{
int x; scanf("%d",&x);
if(x==) continue;
cnt++;
sum[cnt]=sum[cnt-]+x;
} f2++; q[f2]=; n=cnt; for(int i=;i<=n;i++)
{
while()
{
if(f2-f1+<) break;
if(check(q[f1],q[f1+],i)) break;
f1++;
} dp[i]=dp[q[f1]]+(sum[i]-sum[q[f1]])*(sum[i]-sum[q[f1]])+m; while()
{
if(f2-f1+<) break;
if(check2(q[f2-],q[f2],i)) break;
f2--;
} f2++; q[f2]=i;
} printf("%d\n",dp[n]);
}
return ;
}
HDU 3507 Print Article(斜率优化推导)的更多相关文章
- hdu 3507 Print Article(斜率优化DP)
题目链接:hdu 3507 Print Article 题意: 每个字有一个值,现在让你分成k段打印,每段打印需要消耗的值用那个公式计算,现在让你求最小值 题解: 设dp[i]表示前i个字符需要消耗的 ...
- HDU 3507 Print Article 斜率优化
Print Article Time Limit: 9000/3000 MS (Java/Others) Memory Limit: 131072/65536 K (Java/Others)To ...
- hdu 3507 Print Article —— 斜率优化DP
题目:http://acm.hdu.edu.cn/showproblem.php?pid=3507 设 f[i],则 f[i] = f[j] + (s[i]-s[j])*(s[i]-s[j]) + m ...
- 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 ...
- HDU 3507 单调队列 斜率优化
斜率优化的模板题 给出n个数以及M,你可以将这些数划分成几个区间,每个区间的值是里面数的和的平方+M,问所有区间值总和最小是多少. 如果不考虑平方,那么我们显然可以使用队列维护单调性,优化DP的线性方 ...
- ●HDU 3507 Print Article
题链: http://acm.hdu.edu.cn/showproblem.php?pid=3507 题解: 斜率优化DP 一个入门题,就不给题解了,网上的好讲解很多的. 这里就只提一个小问题吧( ...
- HDU 3507 Print Article(DP+斜率优化)
Print Article Time Limit: 9000/3000 MS (Java/Others) Memory Limit: 131072/65536 K (Java/Others) ...
- DP(斜率优化):HDU 3507 Print Article
Print Article Time Limit: 9000/3000 MS (Java/Others) Memory Limit: 131072/65536 K (Java/Others)To ...
- HDU 3507 Print Article(斜率优化DP)
题目链接 题意 : 一篇文章有n个单词,如果每行打印k个单词,那这行的花费是,问你怎么安排能够得到最小花费,输出最小花费. 思路 : 一开始想的简单了以为是背包,后来才知道是斜率优化DP,然后看了网上 ...
随机推荐
- BZOJ4345 POI2016Korale(构造+堆+线段树)
注意到k与n同阶,考虑构造一种枚举子集的方式,使得尽量先枚举较小的子集.首先sort一下,用堆维护待选子集.每次取出最小子集,并加入:1.将子集中最大数ai替换为ai+1 2.直接向子集中添加ai+1 ...
- P2127 序列排序
题目描述 小C有一个N个数的整数序列,这个序列的中的数两两不同.小C每次可以交换序列中的任意两个数,代价为这两个数之和.小C希望将整个序列升序排序,问小C需要的最小代价是多少? 输入输出格式 输入格式 ...
- [洛谷P3946] ことりのおやつ(小鸟的点心)
题目大意:最短路,第$i$个点原有积雪$h_i$,极限雪高$l_i$(即雪超过极限雪高就不可以行走),每秒降雪$q$,ことり速度为$1m/s$,若时间大于$g$,则输出$wtnap wa kotori ...
- 【17.12.22.B】
B 题面描述: 给一个长度为n的序列,a[1], a[2], ... , a[n], 选出连续的k个数,使得这k个数的最大值加这k个数的or值最大. 假设选出的数为a[l], a[l + 1], .. ...
- [codeforces/gym/100431/E]KMP关于border的理解
题目链接:http://codeforces.com/gym/100431/ 考虑到对于一个串β,能cover它的最短的α必然是它的border的某个前缀,或者是这个β本身. 所谓border,就是n ...
- 如何快速的进行sql脚本升级
sql脚本升级即从一个老的脚本升级到一个新的全量的脚本.比如公司有某一个项目,有的客户已经用这个项目了,数据库里面去掉以前的初始化数据外,现在还有了客户自己的数据.但是这个版本中有严重的bug,所以为 ...
- 学习python类
类:Python 类提供了面向对象编程的所有基本特征: 允许多继承的类继承机制, 派生类可以重写它父类的任何方法, 一个方法可以调用父类中重名的方法. 对象可以包含任意数量和类型的数据成员. 作为模块 ...
- Lucene4.6查询时完全跳过打分,提高查询效率的实现方式
由于索引的文件量比较大,而且应用中不需要对文档进行打分,只需要查询出所有满足条件的文档.所以需要跳过打分来提高查询效率.一开始想用ConstantScoreQuery,但是测试发现这个类虽然让所有返回 ...
- SVG布局
http://www.w3cplus.com/html5/nesting-svgs.html
- codefoeces problem 671D——贪心+启发式合并+平衡树
D. Roads in Yusland Mayor of Yusland just won the lottery and decided to spent money on something go ...