[BZOJ3156]防御准备(斜率优化dp)

本文介绍了一种利用动态规划解决特定问题的方法,并通过队列优化来提高算法效率。通过对问题进行反置转换,实现从右向左遍历,重点在于如何通过维护一个单调队列来减少状态转移的时间复杂度。

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

题目描述

传送门

题解

首先把序列反置,然后就变成了都挪向左边,第一个必须建守卫塔
fi表示在i点建守卫塔的费用总和,转移方程: fi=min{fj+si1sj(ci1cj)dj+costi}j<i 其中di表示1~i的距离,ci表示1~i的点数,s表示的是d的前缀和。
注意这里第一个必须选,所以开始的队列不能为0,应该为1。并且最后一个可以不选,单独枚举。

代码

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define LL long long

const int max_n=1e6+5;
const LL INF=1e9;
int n,head,tail,q[max_n];
LL cost[max_n],d[max_n],s[max_n],c[max_n],f[max_n];
LL ans;

inline LL K(int j){return -d[j];}
inline LL B(int j){return f[j]-s[j]+c[j]*d[j];}
inline LL Y(int i,int j){return K(j)*c[i-1]+B(j);}
inline bool cmp(int x1,int x2,int x3){
    LL w1=(K(x1)-K(x3))*(B(x2)-B(x1));
    LL w2=(K(x1)-K(x2))*(B(x3)-B(x1));
    return w1>=w2;
}
int main(){
    scanf("%d",&n);
    for (int i=1;i<=n;++i) scanf("%lld",&cost[i]);
    for (int i=1;i<=n/2;++i) swap(cost[i],cost[n-i+1]);
    d[1]=0; for (int i=2;i<=n;++i) d[i]=d[i-1]+1;
    for (int i=1;i<=n;++i) s[i]=s[i-1]+d[i];
    for (int i=1;i<=n;++i) c[i]=c[i-1]+1;
    head=0;tail=0; q[tail]=1; f[1]=cost[1];
    for (int i=2;i<=n;++i){
        while (head<tail&&Y(i,q[head])>=Y(i,q[head+1])) head++;
        f[i]=Y(i,q[head])+s[i-1]+cost[i];
        while (head<tail&&cmp(i,q[tail-1],q[tail])) tail--;
        q[++tail]=i;
    }
    ans=f[n];
    for (int i=1;i<n;++i)
      ans=min(ans,f[i]+s[n]-s[i]-d[i]*(c[n]-c[i]));
    printf("%lld\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值