斜率dp A

A - Print Article

 HDU - 3507 

今天刚刚学习了一下斜率dp,感觉还ok,主要就是要推这个斜率,然后利用数据结构来优化。

推荐两篇写的比较好的博客,https://www.cnblogs.com/orzzz/p/7885971.html ----> 这个主要学习这个斜率dp的思路

https://www.cnblogs.com/ka200812/archive/2012/08/03/2621345.html ---> 这个主要看代码,中间过程感觉有点问题。

https://blog.csdn.net/qq_37025443/article/details/78986673  --->  这个也建议看看

自己重新理一下这个思路。

sum[i] 表示从1~i 的前缀和

dp[i]表示输到第 i 个字母的最小的花费

所以转移方程很简单 dp[i]=min(dp[j]+(sum[i]-sum[j])^2+M),因为这个j已经输出了,所以下次考虑j+1~i 所以是sum[i]-sum[j] 而不是sum[i]-sum[j-1]

因为这个n有5e5 所有for两次显然会T,这个就可以利用斜率来优化。

令j>k 如果从 j 转移比从k转移更优就需要满足  dp[j]+(sum[i]-sum[j])^2+M<dp[k]+(sum[i]-sum[k])^2+M

上面式子化简之后就是dp[j]+sum[j-1]^2-(dp[k]+sum[k]^2)<2*sum[i]*(sum[j-1]-sum[k])

然后令F[x]=dp[x]-sum[x]^2 

所以就是F[j]-F[k]<sum[i]*2*(sum[j]-sum[k]) 而且要满足  j>k

令G[j,k]=(F[j]-F[k])/(2*(sum[j]-sum[k])

所以如果有 i>j>k 那么如果是G[i,j]>G[j,k] 这样就是一个上凹折线,这个肯定不对

G[i,j]<sum[i],那么就是说i点要比j点优,排除j点。

如果G[i,j]>=sum[i],那么j点此时是比i点要更优,但是同时G[j,k]>G[i,j]>sum[i]。这说明还有k点会比j点更优,同样排除j点。

所以一定是下凸的折现,如果不是的就可以排除。

然后对于G[i,j]>G[j,k]

也有三种情况:

1. G[i,j]>sum[t]>G[j,k]   j 比 i 优,j 比 k 优

2. G[i,j]>G[j,k]>sum[t]  j 比 i 优,k比 j 优

3.sum[t]>G[i,j]>G[j,k]  i 比 j 优 ,j比 k 优

所以我们就用单调队列维护<sum[t] 的最大值即可。

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <vector>
#include <string>
#define inf 0x3f3f3f3f
#define inf64 0x3f3f3f3f3f3f3f3f
using namespace std;
const int maxn = 5e5 + 10;

int que[maxn], dp[maxn];
int sum[maxn];
int n, m;

int up(int i,int j)
{
    return dp[i] + sum[i] * sum[i] - (dp[j] + sum[j] * sum[j]);
}

int down(int i,int j)
{
    return 2 * (sum[i] - sum[j]);
}

int DP(int i,int j)
{
    return dp[j] + (sum[i] - sum[j])*(sum[i] - sum[j]) + m;
}

int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        sum[0] = dp[0] = 0;
        for (int i = 1; i <= n; i++) scanf("%d", &sum[i]), sum[i] += sum[i - 1];
        int tail = 0, head = 0;
        que[tail++] = 0;//因为可能是前面i个全部作为一段才是最小值
        for (int i = 1; i <= n; i++) {//head+1<tail 保证队列里面至少有两个值
            while (head + 1 < tail&&up(que[head + 1], que[head]) <= sum[i] * down(que[head + 1], que[head])) head++;
            dp[i] = DP(i, que[head]);
            while (head + 1 < tail&&up(i, que[tail - 1])*down(que[tail - 1], que[tail - 2]) <= up(que[tail - 1], que[tail - 2])*down(i, que[tail - 1])) tail--;
            que[tail++] = i;
        }
        printf("%d
", dp[n]);
    }
    return 0;
}

  

原文地址:https://www.cnblogs.com/EchoZQN/p/11396635.html