POJ - 3926 Parade(单调队列优化dp)
題目鏈接:點擊查看
題目大意:給出一個n*m的街道,其中有(n+1)*m條街道,每條街道都有一個值,現在我們需要從最下面的任意一點出發,到達最上面的任意一點結束,問如何規劃路線能讓沿途經過的街道的權值和最大,現在多了一個規則,就是橫向的街道每次最多只能走k個單位的距離
題目分析:首先我們在讀入數據的時候可以將行反轉一下,這樣題目就轉換成了從第一行任意一點出發,到最后一行任意一點結束,所能產生的最大貢獻,這一步可有可無,純粹是習慣性的方便計算
然后我們來設計一下動態規劃的dp,轉移方程以及初始條件,因為是一個二維的坐標系,我們可以設dp[i][j]代表到達第i行第j列所能產生的最大貢獻,這個時候轉移方程也就很輕易的就寫出來了:設val[i]是1~i的權值的前綴和
dp[i][j]=max(dp[i-1][j]+val[i][kk]-val[i][j],dp[i-1][j]+val[i][j]-val[i][kkk]);
注意因為我們在拐彎之后,只能單方向前進,所以只有兩種情況,一種是到下一行之后去左邊走距離不超過k的最大權值,另一種是到下一行之后去右邊走距離不超過k的最大權值
因為val是前綴和,可能有的同學會問了,不應該是val[i][j]-val[i][kk-1]嘛,確實正常的前綴和是需要這樣寫的,但這個題目比較特殊,我們觀察一下給出的樣例以及題目中的圖片可以知道,節點的個數總是比街道的個數多一個,換句話說,每條街道都會被兩個節點夾在一起,那么我們不妨在用前綴和的時候,讓每個前綴和都代表一個節點,m個節點加上val[0]就剛好組成了m+1個節點了,這樣一來在這個題目中前綴和查詢區間和就不需要讓左端點減一了
接下來我們的問題是如何快速查找區間最大值,如果暴力找的話肯定會T掉,因為時間復雜度就變成了n*m*m=1e10,用st表會爆內存,因為我計算的內存是需要80M,而這個題目只提供了60M,用線段樹也會被卡常,線段樹每次區間查詢最值會有logn的開銷,這樣時間復雜度雖然優化成了n*m*logm,但大概還是有1e7的時間復雜度,按照理論來說,應該是勉強可以過的,但這個題目時間卡的很緊,如果用單調隊列優化之后也還是需要加一個快讀才能勉強250ms跑過去,所以多出這logm的時間開銷,至少是需要2s的時間才能跑,放在這個題目里,st表無疑會T掉
話說回來,如果是動態查詢區間最值,我們就可以用單調隊列進行優化了,時間復雜度就可以優化為n*m,常數為2,具體實現的話,我們就先討論其中的一種情況吧,dp[i][j]=dp[i-1][j]+val[i][kk]-val[i][j],我們可以發現,dp[i-1][j]和val[i][j]的列坐標是綁定在一起的,所以我們應該將其視為一個整體,對這個整體維護一個非嚴格遞減的單調隊列,這樣在滿足條件的區間內,我們選取最大值來更新dp[i][j]即可,對于kk<=j的情況我們可以這樣從0~m遍歷一遍更新,對于kk>=j的情況我們就從m~0反著遍歷一遍并更新一下答案就可以了,但我們會發現,j=kk的這個點被更新了兩次,其實在這個題目中只是增加了O(1)的時間開銷,并不會影響答案的正確性,所以不用特殊處理了
另外我是今天才知道deque這個stl容器并不是C++11才有的。。所以直接用deque模擬單調隊列會方便很多,這個題目卡了快讀的時間,卻沒有卡stl的時間,有點無語。。
對了記得枚舉每一列m的時候,要從0-m,這個上文中已經提過了,我們是將前綴和分為了m+1個節點分別代表著每個頂點,兩兩頂點之間的才代表著相應的道路
代碼:
deque模擬單調隊列:
#include<iostream> #include<cstdlib> #include<string> #include<cstring> #include<cstdio> #include<algorithm> #include<climits> #include<cmath> #include<cctype> #include<stack> #include<queue> #include<list> #include<vector> #include<set> #include<map> #include<sstream> #include<deque> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=110;template<class T>inline void read(T &res) {char c;T flag=1;while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag; }struct Node {int id,val;Node(int ID,int VAL){id=ID;val=VAL;} };int head,tail;int dp[N][N*N],val[N][N*N],len[N][N*N];void init() {memset(dp,-inf,sizeof(dp));memset(dp[0],0,sizeof(dp[0])); }int main() { // freopen("input.txt","r",stdin);int n,m,k;while(scanf("%d%d%d",&n,&m,&k)!=EOF&&n+m+k){init();for(int i=n+1;i>=1;i--)for(int j=1;j<=m;j++){read(val[i][j]);val[i][j]+=val[i][j-1];}for(int i=n+1;i>=1;i--)for(int j=1;j<=m;j++){read(len[i][j]);len[i][j]+=len[i][j-1];}for(int i=1;i<=n+1;i++){deque<Node>q;for(int j=0;j<=m;j++)//dp[i][j]=dp[i-1][j]+val[i][j]-val[i][kk]{int temp=dp[i-1][j]-val[i][j];while(q.size()&&q.back().val<temp)q.pop_back();q.push_back(Node(j,temp));while(q.size()&&len[i][j]-len[i][q.front().id]>k)q.pop_front();dp[i][j]=max(dp[i][j],q.front().val+val[i][j]);}q.clear();for(int j=m;j>=0;j--)//dp[i][j]=dp[i-1][j]+val[i][kk]-val[i][j]{int temp=dp[i-1][j]+val[i][j];while(q.size()&&q.back().val<temp)q.pop_back();q.push_back(Node(j,temp));while(q.size()&&len[i][q.front().id]-len[i][j]>k)q.pop_front();dp[i][j]=max(dp[i][j],q.front().val-val[i][j]);}}int ans=-inf;for(int i=1;i<=m;i++)ans=max(ans,dp[n+1][i]);printf("%d\n",ans);}return 0; }數組模擬單調隊列:
#include<iostream> #include<cstdlib> #include<string> #include<cstring> #include<cstdio> #include<algorithm> #include<climits> #include<cmath> #include<cctype> #include<stack> #include<queue> #include<list> #include<vector> #include<set> #include<map> #include<sstream> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=110;template<class T>inline void read(T &res) {char c;T flag=1;while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag; }struct Queue {int id,val; }q[N*N];int head,tail;int dp[N][N*N],val[N][N*N],len[N][N*N];void init() {memset(dp,-inf,sizeof(dp));memset(dp[0],0,sizeof(dp[0])); }int main() { // freopen("input.txt","r",stdin);int n,m,k;while(scanf("%d%d%d",&n,&m,&k)!=EOF&&n+m+k){init();for(int i=n+1;i>=1;i--)for(int j=1;j<=m;j++){read(val[i][j]);val[i][j]+=val[i][j-1];}for(int i=n+1;i>=1;i--)for(int j=1;j<=m;j++){read(len[i][j]);len[i][j]+=len[i][j-1];}for(int i=1;i<=n+1;i++){head=0;tail=-1;for(int j=0;j<=m;j++)//dp[i][j]=dp[i-1][j]+val[i][j]-val[i][kk]{int temp=dp[i-1][j]-val[i][j];while(head<=tail&&q[tail].val<temp)tail--;q[++tail].id=j;q[tail].val=temp;while(head<tail&&len[i][j]-len[i][q[head].id]>k)head++;dp[i][j]=max(dp[i][j],q[head].val+val[i][j]);}head=0;tail=-1;for(int j=m;j>=0;j--)//dp[i][j]=dp[i-1][j]+val[i][kk]-val[i][j]{int temp=dp[i-1][j]+val[i][j];while(head<=tail&&q[tail].val<temp)tail--;q[++tail].id=j;q[tail].val=temp;while(head<=tail&&len[i][q[head].id]-len[i][j]>k)head++;dp[i][j]=max(dp[i][j],q[head].val-val[i][j]);}}int ans=-inf;for(int i=1;i<=m;i++)ans=max(ans,dp[n+1][i]);printf("%d\n",ans);}return 0; }?
總結
以上是生活随笔為你收集整理的POJ - 3926 Parade(单调队列优化dp)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: POJ - 2201 Cartesian
- 下一篇: 牛客 - 「土」巨石滚滚(贪心)