1. 程式人生 > >Watching Fireworks is Fun -單調佇列優化DP

Watching Fireworks is Fun -單調佇列優化DP

  • 題意:一個城鎮有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;
    }