Codeforces —— 372C Watching Fireworks is Fun

一道动态规划的题目,通过维护一个单调队列加速决策。
大致思路就是,先把所有的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;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值