CDOJ 1346(斜率DP)

15 篇文章 0 订阅
题意&思路:

题意:把大的区间分段,每一段的有一个花费,求总的最小花费。

这种分段的花费的题,貌似成了斜率dp的一眼题了,上次CF的E题比赛的时候,实力套路一波斜率dp的推导过程,感觉还不错,还学习了下二维的斜率优化。

首先,你假设就按二维来做,然后看是否满足能够去斜率优化,写出二维的转移就是:
Dp[i][j] : 区间 [1,i] 分为 j 段的最小花费

Dp[i][j]=min(dp[k][j1]+cost(k+1,i)),   Cost(k+1,i)=M+(sigmaa[k+1.i])2

然后呢,假设 k1<k2<i ,而且 K2 K1 更优,则:
代入原来的式子,可以推出一个式子,不等式的一边只含有i,另外一边也不含有i:

(dp[k2]+num[k2]2(dp[k1]+num[k1]2))/(2(num[k2]num[k1]))<sum[i]

满足斜率优化的条件,也就是只要满足上面的式子,就表示 K2 K1 更优,那么 K1 就不会更新到后面的值。

所以就用队列来维护一个凸包,进队和出队操作就是:

while(l < r && slope(Q[l+1], Q[l]) <= sum[i]) l ++; // 舍去点
while(l < r && slope(i, Q[r]) <= slope(Q[r], Q[r-1])) r --; // 找到第一个满足构成凸包的点
Q[++r] = i;

代码:
#include <bits/stdc++.h>
#define PB push_back
#define FT first
#define SD second
#define MP make_pair
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int>  P;
const int N = 5e5 + 10,MOD = 7+1e9;
int n, m;
int a[N], sum[N], Q[N];
LL dp[N];
int l, r;
double slope(int i,int j)
{
    if (sum[i] == sum[j])
    return 1e233;
    double up = dp[i] - dp[j] + 1LL*sum[i]*sum[i] - 1LL*sum[j]*sum[j];
    double down = 2 * (sum[i] - sum[j]);
    return up / down;
}
int main()
{
    scanf("%d%d", &n, &m);
    for(int i = 1;i <= n;++ i) scanf("%d", &a[i]), sum[i] = a[i] + sum[i-1];
    l = r = 1;
    Q[r] = 0;
    for(int i = 1;i <= n;i ++)
    {
        while(l < r && slope(Q[l+1], Q[l]) <= sum[i]) l ++;
        int now = Q[l];
        dp[i] = dp[now] + m + 1LL*(sum[i]-sum[now])*(sum[i]-sum[now]);
        while(l < r && slope(i, Q[r]) <= slope(Q[r], Q[r-1])) r --;
        Q[++r] = i;
    }
    printf("%lld\n", dp[n]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值