【题目大意】
中文题面。
需要注意的是AI,BI,TI和题面里的输入顺序不同
【解题思路】
由于bi是常数,所以可以用Σb来减去答案即可。
所以实际上我们维护的是|x-ai|的最小值
状态定义:dp[i][j]表示第j时刻(注意是时刻不是时间,此处的j只表明花出现的顺序而不关心花出现的具体时间)人在i位置时所对应的|x-ai|的最小值
考虑转移方程:dp[i][j]=min(dp[k][j-1]+|i-flower[j]|)其中k满足i-Δt*d<=k<=i+Δt*d(Δt=这一时刻的时间减去上一时刻的时间)
由于|i-flower[j]|与min函数的主体k无关,所以该部分可以从min中取出
所以转移方程转化为->dp[i][j]=min(dp[k][j-1])+|i-flower[j]|
其中min部分可以使用单调队列进行处理
最后枚举终点,求出答案
【代码】
#include<iostream>
#include<string>
#include<cstring>
#include<algorithm>
#include<ctime>
#include<cmath>
#include<cstdio>
#include<cstdlib>
//#include<conio.h>
#include<iomanip>
#define LL long long
//#define LOCAL
using namespace std;
const int N=100011;
const LL INF=2139062143;
LL n,m,d,sigma_s;
LL dp[N][101];
LL ans;
int head,tail;
int tim=0;
struct Flower{
int t;
int f;
}delta[N];
bool cmp(const Flower &A,const Flower &B){
return A.t<B.t;
}
struct Handrum{
int q[N];
bool exist[N];
void Insert(LL x,LL s){
while (head<=tail&&dp[q[tail]][s]>=dp[x][s]) exist[q[tail--]]=false;
q[++tail]=x;
exist[x]=true;
}
void Clear(){
head=0;
tail=-1;
memset(q,0,sizeof(q));
memset(exist,false,sizeof(exist));
}
void Delete(LL x){
if (exist[x]) head++;
}
}que;
int main(){
#ifdef LOCAL
freopen("UESTC1132.in","r",stdin);
#endif
que.Clear();
ans=INF;
sigma_s=0;
scanf("%lld%lld%lld",&n,&m,&d);
for (int i=1;i<=m;++i){
LL pos,value;
scanf("%lld%lld%lld",&pos,&value,&delta[i].t);
delta[i].f=pos;
sigma_s+=value;
}
sort(delta+1,delta+m+1,cmp);
for (int i=1;i<=n;++i){
dp[i][1]=abs(i-delta[1].f);
}
for (int i=2;i<=m;++i){
que.Clear();
LL tmp=delta[i].t-delta[i-1].t;
LL len=tmp*d;
for (int j=1;j<=min(len,n);++j) que.Insert(j,i-1);
for (int j=1;j<=n;++j){
if (j+len<=n) que.Insert(j+len,i-1);
if (j-len>1) que.Delete(j-len-1);
dp[j][i]=dp[que.q[head]][i-1]+abs(j-delta[i].f);
}
}
for (int i=1;i<=n;++i) ans=min(ans,dp[i][m]);
printf("%lld\n",sigma_s-ans);
return 0;
}
【总结】
DP+单调队列