HDU3507--Print Article(斜率优化DP)

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

题意:给出一行数,按从左到右的顺序将这段数分块,每一块的代价为区间和的平方+常数M。求最小代价。

思路:用dp[i]表示到i的最小代价,sum[i]表示前缀和

n^2算法是很好想的,显然是超时的...

假设k<j,考虑从k转移到i和从j转移到i这两种决策

1.dp[i] = dp[k]+(sum[i]-sum[k])^2+M

2.dp[i] = dp[j]+(sum[i]-sum[j])^2+M

如果决策j比k更优,则

dp[j]+sum[i]^2-2*sum[i]*sum[j]+sum[j]^2+M < dp[k]+sum[i]^2-2*sum[i]*sum[k]+sum[k]^2+M

化简一下可得(dp[j]+sum[j]^2-dp[k]-sum[k]^2)/(2*(sum[j]-sum[k]) < sum[i]

令yj = dp[j] + sum[j]^2,xj = 2*sum[j];

可以看出左边是个斜率的表达式,我们这里用g[k,j]表示

于是g[k,j] < sum[i]即选择j比选择k的决策更优.

假如g[k,j] >= g[j,i],那么j永远不会被选择,理由如下:

1.假设g[k,j] < sum[i],那么j比k更优,但是i比j更优

2.假设g[k,j] >= sum[i],那么k比j更优

于是我们想到了一个显然的优化,维护一个斜率上升的栈。每次进来一个新点c之前,从栈顶拿2个元素a,b。如果g[a,b] >= g[b,c]。

那么就把b这个点去掉,循环直到斜率满足g[a,b] < g[b,c].

那...dp[i]怎么求呢...

显然把队列里遍历一遍是肯定能找到解的=_=

但是我们仔细看不等式右边的c[i].在题意中默认是>=0.

假设k<j<i.现在我们假设j比k更优.也就是dp[i] = dp[j]+(sum[i]-sum[j])^2+M.

那么对于一个x>i.dp[x]不可能选择j之间的决策,比如k,为什么呢?

因为sum[x] >= sum[i].如果k的决策比j要更优,则有g[k,j]>sum[x].但这与g[k,j]<=sum[i]不符.

到这里就说得差不多了...如果这题的c[i]可以为负...貌似就必须每次都遍历整个栈了?

感谢http://www.cnblogs.com/ka200812/archive/2012/08/03/2621345.html

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 500080
#define inf 0x3f3f3f3f
int a[maxn],sum[maxn],dp[maxn];
int q[maxn],M;
bool Judge(int a,int b,int Sum)
{
	if(dp[b]+sum[b]*sum[b]-dp[a]-sum[a]*sum[a] < Sum*(2*sum[b]-2*sum[a]))
		return 1;
	return 0;
}
bool Judge1(int a,int b,int c)
{
	if((dp[b]+sum[b]*sum[b]-dp[a]-sum[a]*sum[a])*(2*(sum[c]-sum[b])) >= ((dp[c]+sum[c]*sum[c]-dp[b]-sum[b]*sum[b])*(2*(sum[b]-sum[a]))))	return 1;
	return 0;
}
int  GetAns(int j,int i)
{
	return (dp[j] + (sum[i]-sum[j])*(sum[i]-sum[j]) + M);
}
inline int min(int a,int b,int c)
{
	if(a>b) a = b;
	if(a>c) a = c;
	return a;
}
int main()
{
	//freopen("in.txt","r",stdin);
	//freopen("out.txt","w",stdout);
	int n;
	while(scanf("%d%d",&n,&M)==2)
	{
		for(int i = 1;i <= n;i++)
			scanf("%d",&a[i]);
		sum[0] = dp[0] = 0;
		for(int i = 1;i <= n;i++)
			sum[i] = sum[i-1] + a[i];
		int first = 0,rear = 0;
		q[rear++] = 0;
		for(int i = 1;i <= n;i++)
		{
			//先确定dp[i]
			while(first+1 < rear && Judge(q[first],q[first+1],sum[i]))
				first++;
			dp[i] = GetAns(q[first],i);
			while(first+1 < rear && Judge1(q[rear-2],q[rear-1],i))
				rear--;
			q[rear++] = i;
		}
		printf("%d\n",dp[n]);
	}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值