这题的最直接算法是 O ( n 2 k ) O(n^2k) O(n2k)的暴力Dp
for(int i=1;i<=n;i++)
for(int k=0;k<=sta[i].f;k++)
for(int l=0;l<=sum[i]-k;l++)
f[i][l+k]=min(f[i][l+k],f[i-1][l]+(l)*(l)*(sta[i].x-sta[i-1].x)+sta[i].c*k);
我们想一想如何优化
我们在更新的过程中,对于上一个站的最优解,我们枚举了多次
而且对于上一个站点的范围我们也有要求,我们想到了什么,单调队列
挺板子的单调队列,这里使用双端队列实现,主要是懒。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
const int maxn = 10007,maxm = 505,INF = 0x3f3f3f3f;
long long sum[maxn],dp[maxm][maxn],k,e,n;
struct node{long long x,f,c;}sta[maxn];
bool cmp1(node a,node b){return a.x<b.x;}
int main(){
scanf("%lld%lld%lld",&k,&e,&n);
for(int i=1;i<=n;i++)scanf("%lld%lld%lld",&sta[i].x,&sta[i].f,&sta[i].c),sum[i]=sum[i-1]+sta[i].f;
sta[++n]=(node){e,0,0};
sort(sta+1,sta+1+n,cmp1);
memset(dp,0x3f,sizeof(dp));
dp[0][0]=0;
for(int i=1;i<=n;i++){
deque<int>q;
for(int j=0;j<=k;j++){
while(!q.empty()&&j-q.front() > sta[i-1].f) q.pop_front();// 当前点的储存量不够
if(dp[i-1][j]!=INF){
while(!q.empty()&&dp[i-1][q.back()]-sta[i-1].c*q.back() >= dp[i-1][j]-sta[i-1].c*j )q.pop_back();
q.push_back(j); // 把上一个点的可用解更新,取最优
}
int now=q.front();
if(!q.empty()){dp[i][j]=dp[i-1][now]+(sta[i].x-sta[i-1].x)*j*j+sta[i-1].c*(j-now);}// 更新答案
}
}
cout<<dp[n][k];
}