1. 程式人生 > >【簡潔易懂】CF372C Watching Fireworks is Fun dp + 單調佇列優化 dp優化 ACM codeforces

【簡潔易懂】CF372C Watching Fireworks is Fun dp + 單調佇列優化 dp優化 ACM codeforces

題目大意

一條街道有\(n\)個區域。 從左到右編號為\(1\)到\(n\)。 相鄰區域之間的距離為\(1\)。
在節日期間,有\(m\)次煙花要燃放。 第\(i\)次煙花燃放區域為\(a_i\) ,幸福屬性為\(b_i\),時間為\(t_i\)。\(t_i \leqslant t_{i+1}\)

如果你在第\(i\)次煙花發射時在\(x(1\leqslant x \leqslant n)\)處,你將獲得幸福值\(b_i - | a_i - x |\) (請注意,幸福值可能是負值)。

你可以在單位時間間隔內移動最多\(d\)個單位,但禁止走出主要街道。 此外,您可以在初始時刻(時間等於\(1\)時)處於任意區域,並希望最大化從觀看煙花中獲得的幸福總和。

輸出最大的幸福總和。

題目解答

本題是單調佇列優化\(DP\)的經典題目。

設\(dp[i][j]\)表示第\(i\)次煙花時你位於\(j\)處所能獲得的最大的幸福總和。

而第\(i-1\)次煙花燃放到第\(i\)次煙花燃放所能移動的最大距離為\(h=(t_i-t_{i-1})*d\)。

所以該次燃放後可能獲得的幸福總和由上一次位於\([j-h,j+h]\)處的幾種情形得到。

且\(dp[i][j]=\max\{dp[i-1][k]+b[i]-|a[i]-j|\} \quad k\in [j-h,j+h]\)

即\(dp[i][j]=\max\{dp[i-1][k]\}+b[i]-|a[i]-j| \quad k\in [j-h,j+h]\)

故對於\(j\in[1,n]\) ,要求\(dp[i][j]\)的值只要求解\(dp[i-1]\)陣列位於\([j-h,j+h]\)的最大值,而求解這一步可以用單調佇列解決,複雜度\(O(n)\),即可求解完\(dp[i]\)陣列。

又發現\(dp[i]\)陣列的求解只與\(dp[i-1]\)陣列有關,故這一維可以滾動處理。

\(dp[s1]\)表示源狀態,\(dp[s2]\)表示將求解狀態,求解完交換\(s1\),\(s2\)即可。\(s1,s2\in\{0,1\}\)

單調佇列處理部分

我的程式碼採用雙端佇列\(deque\)處理,較為簡潔。

\(deque\)儲存位置編號

其中\(deque\)中從隊首到隊尾,位置編號嚴格增加,該位置源狀態\(dp\)源狀態值嚴格減少;

處理位置\(i\)(採用程式碼中變數意義)時,將還未處理的小於\(i+h\)的位置依次入隊尾(可能有些元素會被趕出隊尾,因為它們不可能再被使用到),再將小於\(i-h\)的位置依次出隊頭。則隊首所在位置便是源狀態位於\([i-h,i+h]\)的最大值,即可得到現狀態。

原始碼

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=15e4+10;
LL q,m,d,n,s1=0,s2=1;
LL dp[2][maxn];
int main(){
    scanf("%lld%lld%lld%",&n,&m,&d); 
    LL a,b,t,qian_t=1;
    while(m--){
        scanf("%lld%lld%lld%",&a,&b,&t);
        LL h=(t-qian_t)*d;
        qian_t=t;
        deque<int> qu;
        for(int i=1,j=1;i<=n;i++){
            for(;j<=i+h&&j<=n;j++){
                while(!qu.empty()&&dp[s1][qu.back()]<=dp[s1][j])qu.pop_back();
                qu.push_back(j);
            }
            while(!qu.empty()&&qu.front()<i-h)qu.pop_front();
            dp[s2][i]=dp[s1][qu.front()]+b-abs(i-a);
        }
        swap(s1,s2);
    }
    LL maxm=dp[s1][1];
    for(int i=2;i<=n;i++){
        if(dp[s1][i]>maxm)maxm=dp[s1][i];
    }
    printf("%lld",maxm);
}

結束語

歡迎留言!你們的支援與推薦是博主發展的動力