HDU 3507:Print Article(斜率DP)

Print Article


Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others)
Total Submission(s): 10556    Accepted Submission(s): 3227


Problem Description
Zero has an old printer that doesn't work well sometimes. As it is antique, he still like to use it to print articles. But it is too old to work for a long time and it will certainly wear and tear, so Zero use a cost to evaluate this degree.
One day Zero want to print an article which has N words, and each word i has a cost Ci to be printed. Also, Zero know that print k words in one line will cost

M is a const number.
Now Zero want to know the minimum cost in order to arrange the article perfectly.
 

Input
There are many test cases. For each test case, There are two numbers N and M in the first line (0 ≤ n ≤  500000, 0 ≤ M ≤ 1000). Then, there are N numbers in the next 2 to N + 1 lines. Input are terminated by EOF.
 

Output
A single number, meaning the mininum cost to print the article.
 

Sample Input
  
  
5 5 5 9 5 7 5
 

Sample Output
  
  
230


问题概述:一个数列n,你要从左到右输出这个序列中的每一个数,每次连续输出都要消耗连续输出的数字和的平方

加上常数M,问如何输出才能使总消耗最小


http://www.cnblogs.com/kuangbin/archive/2012/08/26/2657650.html

思路:

设dp[i]为前i个数的最小花费,那么有dp[i] = min(dp[j]+(sum[i]-sum[j])²+m)(0<j<i)

可这样循环是n²的,那我们假设k<j<i,从j+1点做起始点比从k+1点做起始点更优,则有公式

dp[j]+(sum[i]-sum[j])²<=dp[k]+(sum[i]-sum[k])²,移项得

((dp[j]+sum[j]*sum[j])-(dp[k]+sum[k]*sum[k]))/(2*(sum[j]-sum[k]))<=sum[i],

那么就得到了斜率表达式g[k,j]=Gp[k,j]/Gd[k,j],如果g[k,j]<=sum[i],则从j+1点做起始点比从k+1点做起始点更优

除此之外,易得如果g[k,j]>=g[j,i]那么j可淘汰。


这样下来,可以确定一个j点,他前面所有的点都不可能比它更优(sum[i]递增),但是后面的怎么办?很显然

此时j点比j+1点优,如果j+1点比j+2点优,那么就没有什么可说的,但如果j+1点没有j+2点优,那么当j+2点入队时,

j+1点就会通过式②被淘汰!这样j点一定是对于当前i点最优的!

那么只要维护一个队列就好,如果当前队尾的两个位置k,j满足g[k,j]<=sum[i],则可以将队尾k弹出,

如果当前队头的两个位置k,j满足g[k,j]>=g[j,i],则可以将队头j弹出,i表示当前遍历到第i个数


#include<stdio.h>
#include<algorithm>
using namespace std;
int m, sum[500005], q[500005], dp[500005];
int Getup(int x, int y)
{
	return dp[y]+sum[y]*sum[y]-dp[x]-sum[x]*sum[x];
}
int Getdown(int x, int y)
{
	return 2*(sum[y]-sum[x]);
}
int main(void)
{
	int n, i, b, e;
	while(scanf("%d%d", &n, &m)!=EOF)
	{
		sum[0] = dp[0] = 0;
		for(i=1;i<=n;i++)
		{
			scanf("%d", &sum[i]);
			sum[i] += sum[i-1];
		}
		b = e = 0;
		q[e++] = 0;
		for(i=1;i<=n;i++)
		{
			while(b+1<e && Getup(q[b], q[b+1])<=sum[i]*Getdown(q[b], q[b+1]))  b++;
			dp[i] = dp[q[b]]+(sum[i]-sum[q[b]])*(sum[i]-sum[q[b]])+m;
			while(b+1<e && Getup(q[e-1], i)*Getdown(q[e-2], q[e-1])<=Getup(q[e-2], q[e-1])*Getdown(q[e-1], i))  e--;
			q[e++] = i;
		}
		printf("%d\n", dp[n]);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值