您的位置:首页 > 其它

POJ 3926 Parade 单调队列优化DP

2014-07-31 10:45 477 查看
来源:http://poj.org/problem?id=3926

题意:行n <= 100, 列m <= 10000,类似于数字三角形,一个人要从底下往上走,每层中可以左右走,但选定方向不能回头(向左不能再向右),每经过一段获得该段的一个值,并走了该段的距离,在同一层走的距离不能超过k。问走到最顶头,获得的总值最大是多少。

分析:dp[i][j]表示走到第i行第j列,获得的值最大为多少。则dp[i][j] = max(dp[i+1][p] + sum(p to j)),sum(p to j)表示第i行从第p列到第j列的值的和,同时p需要满足abs(p-j) <= k。前缀和处理sum,枚举i,j,p,这样是o(nm^2)的复杂度,超时。

如果我们只考虑从左往右走,那么就是类似求有长度上限的最大子段和的模型了。sum(k)表示第i行前k列的前缀和,改写转移方程 dp[i][j] = max(dp[i+1][p] + sum(j) - sum(p)) = max(dp[i+1][p] - sum(p)) + sum(j),这下很清楚了,前一项就可以用单调队列来维护,使得状态转移的复杂度降到均摊o(1)。然后从右往左走再做一遍就可以了。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

int n, m, k;
int a[120][10100], b[120][10100], dp[120][10100];
struct que{
int v, d;
} q[10100];
inline int in(){
char ch = getchar();
while((ch < '0' || ch > '9') && ch != '-') ch = getchar();
bool flag = false;
if (ch == '-'){
flag = true;
ch = getchar();
}
int ans = 0;
while(ch >= '0' && ch <= '9'){
ans = ans*10 + ch-'0';
ch = getchar();
}
if (flag) return -ans;
return ans;
}
int main()
{
while(scanf("%d%d%d", &n, &m, &k) && (n+m+k))
{
for (int i = 0; i <= n; i++)
for (int j = 0; j < m; j++)
a[i][j] = in();
for (int i = 0; i <= n; i++)
for (int j = 0; j < m; j++)
b[i][j] = in();
for (int j = 0; j <= m; j++)
dp[n+1][j] = 0;
for (int i = n; i >= 0; i--){
int sum, head, tail, dis;
sum = head = tail = dis = 0;
dp[i][0] = dp[i+1][0];
q[tail].v = dp[i][0];
q[tail++].d = 0;
for (int j = 1; j <= m; j++){
sum += a[i][j-1];
dis += b[i][j-1];
while(head < tail && dis - q[head].d > k) head ++;
while(head < tail && q[tail-1].v <= dp[i+1][j] - sum) tail --;
q[tail].d = dis;
q[tail++].v = dp[i+1][j] - sum;
dp[i][j] = q[head].v + sum;
}
sum = head = tail = dis = 0;
q[tail].v = dp[i+1][m];
q[tail++].d = 0;
for (int j = m-1; j >= 0; j--){
sum += a[i][j];
dis += b[i][j];
while(head < tail && dis - q[head].d > k) head ++;
while(head < tail && q[tail-1].v <= dp[i+1][j] - sum) tail --;
q[tail].d = dis;
q[tail++].v = dp[i+1][j] - sum;
if (q[head].v + sum > dp[i][j]) dp[i][j] = q[head].v + sum;
}
}
int ans = 0;
for (int i = 0; i <= m; i++)
if (dp[0][i] > ans) ans = dp[0][i];
printf("%d\n", ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: