题目大意:
一个城镇有n个区域,从左到右1-n,每个区域之间距离1个单位距离。节日中有m个烟火要放,给定放的地点a[ i ]、时间t[ i ] ,如果你当时在区域x,那么你可以获得b[ i ] - | a[ i ] - x |的happiness 。你每个单位时间可以移动不超过d个单位距离,你的初始位置是任意的,求你通过移动能获取到的最大的happiness值。
我感觉很难,而且其实也不是特别熟悉单调队列,对于单调队列的运用也不是特别熟练。
这个题目首先你要找状态,这个状态就是时间和位置,所以我们可以先定义 dp[i][j] 表示第 i 时刻在位置 j 的最大幸福值。
但是这个时间有 1e9 不过只有300个物品,就是可以离散化一下,就只有300了,但是位置有150000 这个放进dp数组还是超时了。
不过每一个时间只和上一个时间有关系,所以这个时候就可以用滚动数组了,所以最后就是dp[2][2e5]
这个题目为什么要用单调队列呢,因为如果不是同一时刻绽放的烟花,是有移动距离的,这一段距离的最大幸福度就可以用单调队列来找到。
这个就是我们枚举每一个 j 的位置,上一个烟花在 j 两边左右为 d*t 的距离绽放都是可以让这一个烟花在 j 位置绽放。
#include <cstdio> #include <cstdlib> #include <cstring> #include <queue> #include <vector> #include <algorithm> #include <string> #include <iostream> #define inf 0x3f3f3f3f #define inf64 0x3f3f3f3f3f3f3f3f using namespace std; const int maxn = 2e5 + 10; typedef long long ll; //首先枚举每一个物品,然后再去dp转移找到这个物品的最优解,就是abs(a[i]-x)的最小值 //具体就是用一个来表示时间的转移,然后对于每一个物品,如果这个时间恰好是烟花绽放的时间,那就直接从上一个转移过来 //如果不是,就是说明有一段路可以走,那就用单调队列,维护每一个点的一段区间的最大值 ll que[maxn], dp[2][maxn]; ll a[maxn], b[maxn], t[maxn]; int main() { ll n, m, k; scanf("%lld%lld%lld", &n, &m, &k); for (int i = 1; i <= m; i++) scanf("%lld%lld%lld", &a[i], &b[i], &t[i]); ll pretime = t[1]; int id = 0; for(int i=1;i<=m;i++) { if(pretime==t[i]) { for(int j=1;j<=n;j++) { dp[id][j] = dp[id ^ 1][j] + b[i] - abs(a[i] - j); } } else { ll x = t[i] - pretime; pretime = t[i]; int f1 = 1, t1 = 0; int r = 1; for(int j=1;j<=n;j++) { while(r<=n&&r<=x*1ll*k+j) { while (t1 >= f1 && dp[id ^ 1][r] > dp[id ^ 1][que[t1]]) t1--; que[++t1] = r++; } while (f1 <= t1 && que[f1] < j - x * k) f1++; dp[id][j] = dp[id ^ 1][que[f1]] + b[i] - abs(a[i] - j); } } id ^= 1; } ll ans = -inf64; for (int i = 1; i <= n; i++) { ans = max(ans, dp[id ^ 1][i]); } printf("%lld ", ans); return 0; }