洛谷p3800(單調隊列優化DP)
題目背景
據說在紅霧異變時,博麗靈夢單身前往紅魔館,用十分強硬的手段將事件解決了。
然而當時靈夢在Power達到MAX之前,不具有“上線收點”的能力,所以她想要知道她能收集多少P點,然而這個問題她答不上來,於是她找到了學OI的你。
題目描述
可以把游戲界面理解成一個N行M列的棋盤,有K個格子上有P點,其價值為val(i,j)
初始靈夢可以選擇在第一行的任意一個格子出發,每秒她必須下移一格。
靈夢具有一個左右移動的速度T,可以使她每秒向左或右移動至多T格,也可以不移動,並且不能折返。移動可視為瞬間完成,不經過路途上的點,只能獲得目標格子的P點。
求最終她能獲得的POWER值最大是多少?
輸入輸出格式
輸入格式:
第一行四個數字,N,M,K,T
接下來K行每行3個數字x,y,v,代表第x行第y列有一個val為v的P點,數據保證一個格子上最多只有1個P點。
輸出格式:
一個數字
輸入輸出樣例
輸入樣例#1: 復制
3 3 4 1
1 1 3
1 2 1
2 2 3
3 3 3
輸出樣例#1: 復制
9
說明
對於40%的測試點,1<=N,M,T,K<=200
對於100%的測試點,1<=N,M,T,K<=4000
v<=100,N,M,K,T均為整數
題解:
看完題目,可發現這個題目像數塔,但是因為有速度,在一行可以左右移,所以可推出狀態轉移方程f[i,j]=max{f[i,j-k]~f[i,j+k],},所以復雜度為i*j*k,看范圍,會超時。再觀察題目,發現對於每個上一行,在一個區間內找一個最大值,且一格一格移動,聯想到‘掃描’,可以用單調隊列優化。先處理出k范圍的隊列(里面畢竟要先有數吧)(預處理)之后才能進行刪頭處理。再對第i行每個狀態進行處理(1~j)可用滾動數組。此處單調隊列與導彈攔截的優化不同,這是一個一個移的 |
var m1,s,n,m,i,k,j,tt,x,y,z,t,h,max,l:longint; ans:int64; q,f,dp,id:array[-10..100000]of longint;//q隊列,id下標數組,dp滾動數組(協助存上一層數),f答案數組 a:array[-1..4000,-1..4000]of longint; begin readln(n,m,s,L); for i:=1 to s do begin readln(x,y,z); a[x,y]:=z; end; for i:=1 to m do f[i]:=a[1,i]; for i:=2 to n do begin h:=1;t:=1; q[1]:=f[1];id[1]:=1; for j:=2 to l do begin while (f[j]>q[t])and(h<=t)do dec(t); inc(t); q[t]:=f[j];id[t]:=j; end; k:=l;//dp[1]=q[h]+a[1,i]; for j:=1 to m do //you wrong here這里我調了好久,本來以為1已經計算過了,但不知道為什么沒有,2改成1 就過了,所以dp盡量用記憶化 begin if k+1<=m then begin inc(k); while (q[t]<f[k])and(h<=t) do dec(t); inc(t); q[t]:=f[k];id[t]:=k; end; begin while id[h]<j-l do inc(h); dp[j]:=q[h]+a[i,j]; end; end; for j:=1 to m do begin f[j]:=dp[j]; if max<f[j] then max:=f[j]; end; end; writeln(max); end.