标签:
题意:
给你n*m的图,起始位置在第一行的第x个位置,现在你可以选择一个方向至多走T个位置然后走向下一行,直到第n行
路过的格子里的值总和最大是多少
题解:
首先想到dp[i][j]表示到达当前(i,j)格子的最大答案,那么最后答案显然了
思考如何得到(i,j)的最优答案
他可以是从左边上一层走下来再向右走到j位置,且走过不超过T,也可以是右边
对于左边:dp[i][j] = dp[i-1][k] - sum[i][k-1] + sum[i][j];
dp[i-1][k] - sum[i][k-1]这一块是上一层,和当前层没有任何关系,我们可以预处理啊
那么我们维护一个大小T的单调队列,左边右边扫一波就好了
#include <iostream> #include <algorithm> #include <cstdio> #include <cstring> #include <cmath> #include<queue> using namespace std; const int N = 120, M = 1e4+20, mod = 1000000007,inf = 2e9; typedef long long ll; int n,m,x,t,a[N][M],sum[N][M],dp[N][M]; int main() { while(scanf("%d%d%d%d",&n,&m,&x,&t)!=EOF) { for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&a[i][j]); for(int i=1;i<=n;i++) { sum[i][0] = 0; for(int j=1;j<=m;j++) sum[i][j] = sum[i][j-1] + a[i][j]; } for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) dp[i][j] = -inf; for(int i=x;i>=1&&i>=x-t;i--) dp[1][i] = sum[1][x] - sum[1][i-1]; for(int i=x;i<=m&&i<=x+t;i++) dp[1][i] = sum[1][i] - sum[1][x-1]; deque<int >q; for(int i=2;i<=n;i++) { //从左向右 while(!q.empty()) q.pop_back(); dp[i][1] = dp[i-1][1] + a[i][1]; q.push_back(1); for(int j=2;j<=m;j++) { while(!q.empty()&&j-q.front()>t) q.pop_front(); int now = dp[i-1][j] - sum[i][j-1]; while(!q.empty()&&dp[i-1][q.back()]-sum[i][q.back()-1]<=now) q.pop_back(); q.push_back(j); int pos = q.front(); dp[i][j] = max(dp[i][j],dp[i-1][pos]-sum[i][pos-1]+sum[i][j]); } while(!q.empty()) q.pop_back(); q.push_back(m); dp[i][m] = max(dp[i][m],dp[i-1][m]+a[i][m]); for(int j=m-1;j>=1;j--) { while(!q.empty()&&q.front()-j>t) q.pop_front(); int now = dp[i-1][j] + sum[i][j]; while(!q.empty()&&dp[i-1][q.back()]+sum[i][q.back()]<=now) q.pop_back(); q.push_back(j); int pos = q.front(); dp[i][j] = max(dp[i][j],dp[i-1][pos]+sum[i][pos]-sum[i][j-1]); } } int ans = -inf; for(int i=1;i<=m;i++) ans = max(ans,dp[n][i]); printf("%d\n",ans); } return 0; }
HDU 4374 One hundred layer DP的单调队列优化
标签:
原文地址:http://www.cnblogs.com/zxhl/p/5525360.html