HDU 3507 Print Article

题目链接

斜率DP的入门题。

题意很清楚,就是输出序列a[n],把连续的单词放在同一行输出的费用是连续输出的单词费用和的平方加上常数M

一行的费用为 cost=(C[i])2+M

让我们求这个费用的最小值。

Si Ci 的前缀和。

设dp[i]表示输出前i个单词的最小费用,那么有如下的DP方程:

dp[i]=min(dp[j]+(SiSj)2)+M (0<j<i)

我们首先假设在算 dp[i] 时, k<j , j 点比k点优。

也就是 dp[j]+(SiSj)2<=dp[k]+(SiSk)2;

所谓 j k优就是DP方程里面的值更小

对上述方程进行整理很容易得到:

(dp[j]+S2j)(dp[k]+S2k)(SjSk)<=2Si
注意整理中要考虑下正负,涉及到不等号的方向。

左边我们发现如果令:

yj=dp[j]+S2j
xj=Sj

那么就变成了斜率表达式: yjykxjxk<=sum[i];

而且不等式右边是递增的。

所以我们可以看出以下两点:我们令 g[k,j]=yjykxjxk

第一:如果上面的不等式成立,那就说 j k优,而且随着i的增大上述不等式一定是成立的,也就是对 i 以后算DP值时,j都比 k 优。那么k就是可以淘汰的。

第二:如果 k <j< i 而且 g[k,j]>g[j,i] 那么 j 是可以淘汰的。

假设 g[j,i]<sum[i]就是 i j优,那么 j 没有存在的价值

相反如果 g[j,i]>sum[i] 那么同样有 g[k,j]>sum[i] 那么 k j优 那么 j <script type="math/tex" id="MathJax-Element-518">j</script> 是可以淘汰的

所以这样相当于在维护一个下凸的图形,斜率在逐渐增大。

通过一个队列来维护。

#include <bits/stdc++.h>
using namespace std;
#define prt(k) cerr<<#k" = "<<k<<endl
typedef long long ll;
const ll inf = 0x3f3f3f3f;
const int N = 505000;
int M;
ll S[N];
int n;
ll dp[N];
ll Y(int j)
{
    return dp[j] + S[j] * S[j];
}
ll X(int j)
{
    return  S[j];
}
bool cmp(int k, int j, int i) // j tao tai
{   // k < j < i
    return (Y(j) - Y(k)) * (X(i) - X(j)) >=
    (Y(i) - Y(j)) * (X(j) - X(k));
}
int q[N];
ll sqr(ll x)
{
    return x * x;
}
int main()
{
    while (scanf("%d%d", &n, &M)==2) {
        S[0] = 0;
        for (int i=1;i<=n;i++) {
            int c; scanf("%d", &c);
            S[i] = S[i-1] + c;
        }
        dp[0] = 0;
        int head = 0, tail = -1;
        q[++tail] = 0;
        for (int i=1;i<=n;i++) {
            while (tail - head + 1 >= 2  ) {
                int k = q[head];
                int j = q[head+1];
                if (Y(j) - Y(k)<=2*S[i]*(X(j) - X(k))) {
                    head++;
                }
                else break;
            }
            int j = q[head];
            dp[i] = dp[j] + sqr(S[i] - S[j]) + M;
            while (tail - head + 1 >= 2) {
                int k = q[tail - 1];
                int j = q[tail];
                if (cmp(k, j, i)) tail--;
                else break;
            }
            q[++tail] = i;
        }
        printf("%I64d\n", dp[n]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值