-
G - Watching Fireworks is Fun
- CodeForces - 372C
- 题意:一个城镇有n个区域,从左到右1-n,每个区域之间距离1个单位距离。节日中有m个烟火要放,给定放的地点a[ i ]、时间t[ i ] ,如果你当时在区域x,那么你可以获得b[ i ] - | a[ i ] - x |的happiness 。你每个单位时间可以移动不超过d个单位距离,你的初始位置是任意的,求你通过移动能获取到的最大的happiness值。
- 思路: 首先设dp[i][ j ]为到放第i个烟花的时候站在j的位置可以获得的最大happiness。那么我们可以很容易写出转移方程:
- dp[ i ] [ j ] =max(dp[ i - 1] [ k ]) + b[ i ] - | a[ i ] - j | ,其中 max(1,j-t*d)<=k<=min(n,j+t*d) 。由于是求一段区间的最小值,我们可以想到用单调队列维护,维护一个单调减的队列,并且需要滚动数组优化。单调队列解决的是max(dp[ i - 1] [ k ])如何取出一个对当前j合法的k,所以现在是每个i也就是每个fire都有一个对应自己的队列,并且位置不断后移j可能合法的k也随之往后移,如果新来的合法的比队列中的happiness大了那么队列中原有的值就成为无用的了可以出队了。但是如果新来的happine小也得入队因为对于现在与后面的j未必能够到达前面happin更大的k所以这个小的有可能是对于某个j来说合法的k中最大的。这也就是单调队列特性,更优可以把之前的无效的踢掉但是不更优也有可能是答案必须入队。去最值得时候直接从队首访问即可并且注意k的有效性。
-
#include<bits/stdc++.h> using namespace std; #define ll long long #define maxn 156666 struct node { ll a,b,t; } fire[333]; bool cmp(node c,node d) { return c.t<d.t; } ll n,m,d,pretime,gd,dt,k; ll dp[2][maxn],que[maxn]; int main() { scanf("%lld%lld%lld",&n,&m,&d); for(int i=0; i<m; i++) scanf("%lld%lld%lld",&fire[i].a,&fire[i].b,&fire[i].t); sort(fire,fire+m,cmp); memset(dp,0,sizeof(dp)); pretime=fire[0].t; for(int i=0; i<m; i++) { int head=1,tail=0; k=1; if(pretime==fire[i].t) for(int j=1; j<=n; j++) dp[gd][j]=dp[gd^1][j]+fire[i].b-abs(fire[i].a-j); else { dt=fire[i].t-pretime; pretime=fire[i].t; for(int j=1; j<=n; j++) { while(k<=n&&k<=j+d*dt) { while(head<=tail&&dp[gd^1][k]>dp[gd^1][que[tail]]) tail--; que[++tail]=k++; } while(head<=tail&&j-dt*d>que[head]) head++; dp[gd][j]=dp[gd^1][que[head]]+fire[i].b-abs(fire[i].a-j); } } gd^=1; } ll ans=-1e17; for(int i=1; i<=n; i++) ans=max(ans,dp[gd^1][i]); printf("%lld\n",ans); return 0; }
G - Watching Fireworks is Fun -单调队列优化DP
猜你喜欢
转载自blog.csdn.net/BePosit/article/details/83214110
今日推荐
周排行