G - Watching Fireworks is Fun -单调队列优化DP

  •  
  • 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;
    }
  •  

猜你喜欢

转载自blog.csdn.net/BePosit/article/details/83214110
今日推荐