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 是可以淘汰的

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

通过一个队列来维护。

#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;
}

你可能感兴趣的:(dp,单调队列,dp优化)