一道动态规划的题目,通过维护一个单调队列加速决策。
大致思路就是,先把所有的b[i]加起来,因为计算式中b[i]跟决策没关系,然后反过来求|a[i]-x|的和的最小值即可。
至于最小值的求解,以每个t[i]时刻为基准,枚举每个观赏地点,计算从t[i-1]到t[i]可以移动的格数从而得出可以移动的范围,在这个范围里面找最小值加过去就行了。因为是枚举观赏地点,所以可以发现移动的范围相当于一个滑动窗口,随着枚举向右移动,用单调队列维护即可。
/*
Codeforces 372C
Author: hongrock
*/
#include<cstdio>
#include<cstring>
#define LL __int64
#define MAXN 150001
int n, m, d, i, j, x, l, r, p[MAXN], head, tail, a[300], b[300], t[300];
LL sum, tmp, y, dp[2][MAXN], Q[MAXN];
bool f;
int myabs(int num){
if(num<0) return -num;
return num;
}
int main(){
while(~scanf("%d %d %d", &n, &m, &d)){
sum=0;
for(i=0; i<m; i++){
scanf("%d %d %d",a+i,b+i,t+i);
sum+=b[i];
}
f = 0;
for(i=1; i<=n; i++) dp[0][i]=myabs(i-a[0]);
for(i=1; i<m; i++){
y = (LL)d;
y *= (t[i]-t[i-1]);//这里先转成LL,因为有可能超过int范围
if(y>n) y=n;
x = (int)y;
head=tail=0;
for(j=1;j<=x;j++){
tmp = dp[f][j];
while(head<tail && tmp<Q[tail-1]){
tail--;
}
Q[tail]=tmp;
p[tail++]=j;
}
for(j=1; j<=n; j++){
l = j-x;
if(l<=1) l=1;
while(head<tail && p[head]<l) head++;
r = j+x;
if(r<=n){
tmp=dp[f][r];
while(head<tail && tmp<Q[tail-1]){
tail--;
}
Q[tail]=tmp;
p[tail++]=r;
}
dp[f^1][j]=Q[head]+myabs(j-a[i]);
}
f^=1;
}
tmp = dp[f][1];
for(i=2; i<=n; i++)
if(dp[f][i]<tmp) tmp=dp[f][i];
printf("%I64d\n", sum-tmp);
}
return 0;
}