HDU-3507 Print Article(斜率优化DP)

本文介绍了一种利用斜率优化动态规划的方法来解决HDU-3507打印机问题,旨在在保证文章打印质量的同时,最小化成本。通过维护一个队列,确保队列中点之间的斜率关系,从而找到最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:

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的序列,请你将这个序列分段,每一段序列的价值为,($\sum_l^r$val_i)^2+M,最终使得这些段的价值和最小,输出这个最小值。

解题思路:

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

按照这样的转移方程,就能求得到答案,但是这样转移的话时间复杂度为(n^2),显然是要超时的。

所以我们要降下一维。

我们设当a < b<i时, dp[i]从dp[b]转移过来会优于从dp[a]转移过来。

那么我们可以得到

                                           dp[b]+(sum[i]-sum[b])^2+M < dp[a]+(sum[i]-sum[a])^2+M

化简可得

                                                      \frac{(dp[b]+sum^2[b])-(dp[a]+sum^2[a])}{sum[b]-sum[a]}<2*sum[i]

也就是说,如果a,b两点的斜率满足这个不等式,那么从b点转移就优于a点,并且由于当k>i时,2*sum[k]>=2*sum[i],即sum[]函数是不减的,那么,在i之后b点就永远优于a点,那么在从i之后a点对于这个问题的求解就已经没有意义了。

我们维护一个队列,在这个队列中,对于每一个新的点i,将队列头中,与下一个点构成斜率小于等于2*sum[i]的点不要了,因为这些点在以后的转移中都是不如它的下一个点的。

而当我们要将当前元素放进队列尾的时候,考虑这样一个问题

我们要加入i点,如果i点和队列尾部的点构成这样上凸的形状的话,我们称尾部的点同样是没有意义的

如果 tail-1与tail构成的斜率大于2*sum[k],那么tail-1比tail优,

如果 tail-1与tail构成的斜率小于2*sum[k],那么tail比tail-1优,但是同样会有 tail与i构成的斜率小于2*sum[k],那么i比tail优

所以tail是没有意义的,舍去。

这样,在这个队列里的两点间斜率,后面的斜率就一定会比前面的大

那么 队列中所以点与之前点构成的斜率都会>=队列首和队列第二个元素点构成的斜率>=2*sum[i],那从队列首的那个元素转移过来岂不就是最优解了!

#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
#include<queue>
#include<set>
#include<map>
#include<cmath>

using namespace std;
const long long inf = 0x3f3f3f3f;
const int maxn = 500005;
int n;
long long m;
long long num[maxn];
long long sum[maxn],dp[maxn];
int q[maxn];

long long getup(int b, int a)
{
    return dp[b]+sum[b]*sum[b]-dp[a]-sum[a]*sum[a];
}
long long getdown(int b, int a)
{
    return sum[b]-sum[a];
}

int main()
{
    while(scanf("%d%lld", &n, &m)!=EOF)
    {
        for(int i = 1; i <= n; i++)
        {
            scanf("%lld", &num[i]);
            sum[i] = sum[i-1]+num[i];
        }
        int head = 1,tail = 1;
        q[1] = 0;
        for(int i = 1; i <= n; i++)
        {
            while(head<tail&&getup(q[head+1],q[head])<=2*sum[i]*getdown(q[head+1],q[head])) head++;
            dp[i] = dp[q[head]]+(sum[i]-sum[q[head]])*(sum[i]-sum[q[head]])+m;
            while(head<tail&&getup(q[tail],q[tail-1])*getdown(i,q[tail])>=getup(i,q[tail])*getdown(q[tail],q[tail-1])) tail--;
            q[++tail] = i;
        }
        printf("%lld\n", dp[n]);
    }
    return 0;
}

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值