[SCU 4513] 先锋看烟花 (数据结构优化DP)

本文探讨了一种利用稀疏表解决动态规划问题的方法,即最大化角色在特定时间点的幸福度。通过优化数据结构,将复杂度降低至O(NM),适用于路径规划和资源收集等问题。

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

SCU - 4513

一条路上有 N个房子,一共有 M个烟花
ti时刻,第 ai个房子会放一个价值为 bi的烟花
对在 cur位置的先锋会产生 biabs(aicur)的幸福度
其中幸福度可以为负数
先锋每个单位时间最多可以移动 D的距离
问所有烟花放完之后,先锋的幸福度最大为多少


首先朴素的想法是暴力模拟这个过程
用 dp[i][j]表示第 i秒,处于 j位置的先锋能获得的最大幸福度
在没有烟花的时刻,先锋可以左右移动 D的距离
所以向左右走2*D,以最优答案来更新周围的值

经过模拟可以发现,在没有烟花的时候
某点的答案会其实就等于某段区间内的最大值
并且这段区间随着时间地增长会变大

所以对放烟花的时间排序,在放烟花的时刻,扫一遍数组更新答案
在没放烟花的空闲时刻,算出到下一个烟花绽放时刻之前,
从任意一点出发,先锋能移动的最大范围
用稀疏表求出此范围内的最优答案,然后再暴力扫一遍数组更新答案

总的复杂度 O(NMlog(N))
如果使用单调队列维护最大值,还可以优化到 O(NM)

#pragma comment(linker, "/STACK:102400000,102400000")
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include <queue>
#include <cctype>
using namespace std;
typedef pair<int,int> Pii;
typedef long long LL;
typedef unsigned long long ULL;
typedef double DBL;
typedef long double LDBL;
#define MST(a,b) memset(a,b,sizeof(a))
#define CLR(a) MST(a,0)
#define Sqr(a) (a*a)

const int maxn=15e4+10;
const LL INF=0x3f3f3f3f3f3f3f3fLL;
struct SparseTable
{
    LL **spas,tlog;
    SparseTable(LL*,int);
    ~SparseTable();
    LL query(int,int);
};
int N,M,D;
LL res[maxn];
LL dp[maxn];
LL ai[500],bi[500],ti[500];

void pause(const char *str)
{
    fflush(stdin);
    puts(str);
    getchar();
}

int main()
{
    #ifdef LOCAL
    freopen("in.txt", "r", stdin);
//  freopen("out.txt", "w", stdout);
    #endif

    int T;
    scanf("%d", &T);
    for(int ck=1; ck<=T; ck++)
    {
        scanf("%d%d%d", &N, &M, &D);
        for(int i=1; i<=M; i++) 
        {
            scanf("%lld%lld%lld", &ai[i], &bi[i], &ti[i]);
            ai[i]--;
        }
        LL ans=-INF;
        CLR(res);
        SparseTable *st=new SparseTable(res,N);
        int np=1;
        while(np<=M)
        {
            LL nt=ti[np];
            LL dist=(LL)(ti[np]-ti[np-1])*D;        
            for(int j=0; j<N; j++)
            {
                LL l=max((LL)0,(LL)j-dist), r=min((LL)N-1,(LL)j+dist);
                res[j]=st->query(l,r);
            }
            while(ti[np]==nt&&np<=M)
            {
                for(int j=0; j<N; j++) res[j]+=bi[np]-abs(ai[np]-j);
                np++;
            }
            st->~SparseTable();
            st=new SparseTable(res,N);
        }
        st->~SparseTable();
        for(int i=0; i<N; i++) ans=max(ans, res[i]);
        printf("%lld\n", ans);
    }
    return 0;
}

SparseTable::SparseTable(LL arry[],int len)
{
    tlog=log2(len);
    spas=new LL*[tlog+2];
    for(int i=0; i<=tlog; i++) spas[i]=new LL[len-(1<<i)+1];
    for(int i=0; i<len; i++) spas[0][i]=arry[i];

    for(int j=1; j<=tlog; j++)
    {
        int tlen=1<<j-1,nlim=len-(1<<j)+1;
        for(int i=0; i<nlim; i++)
        {
            spas[j][i]=max(spas[j-1][i],spas[j-1][i+tlen]);
        }
    }
}

SparseTable::~SparseTable()
{
    for(int i=0; i<=tlog; i++) delete[] spas[i];
    delete[] spas;
}

LL SparseTable::query(int l, int r)
{
    int flr=log2(r-l+1);
    int nlen=1<<flr;
    return max(spas[flr][l],spas[flr][r-nlen+1]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值