hdu 4374 One hundred layer 单调队列优化dp

One hundred layer

Time Limit: 1000ms
Memory Limit: 65536KB
This problem will be judged on HDU.
Original ID: 4374
64-bit integer IO format: %I64d Java class name: Main

Now there is a game called the new man down 100th floor. The rules of this game is:
  1. At first you are at the 1st floor. And the floor moves up. Of course you can choose which part you will stay in the first time.
  2. Every floor is divided into M parts. You can only walk in a direction (left or right). And you can jump to the next floor in any part, however if you are now in part “y”, you can only jump to part “y” in the next floor! (1<=y<=M)
  3. There are jags on the ceils, so you can only move at most T parts each floor.
  4. Each part has a score. And the score is the sum of the parts’ score sum you passed by.
Now we want to know after you get the 100th floor, what’s the highest score you can get.

Input

The first line of each case has four integer N, M, X, T(1<=N<=100, 1<=M<=10000, 1<=X, T<=M). N indicates the number of layers; M indicates the number of parts. At first you are in the X-th part. You can move at most T parts in every floor in only one direction.
Followed N lines, each line has M integers indicating the score. (-500<=score<=500)

Output

Output the highest score you can get.

Sample Input

3 3 2 1
7 8 1
4 5 6
1 2 3

Sample Output

29

中文题意

给一个n*m的举证,从第一行的x处,走到最后一行,求途中经过的数字和最大,每行最多能朝一个方向走t步.

题解

很容易想到dp方程, dp[i][j]代表走到第i行第j列,并且下一步到第i+1行的最大值
先从左到右 : dp[i][j] = max(dp[i-1][k] + sum[i][j] - sum[i][k-1])
再从右到左 : dp[i][j] = max(dp[i-1][k] + sum[i][k] - sum[i][j-1])
(sum[i][j]代表第i行的前j个的和)
很明显如果暴力枚举每一个k的话,会超时,再仔细想想,其实对于每个dp[i][j], 我们需要的
仅仅是第i-1行在t步范围内的最大的dp[i-1][k]-sum[i][k-1], 然后把这个最大值加上sum[i][j]
就是从左到右的dp[i][j]的答案。这时候就可以想到用一个单调队列维护这个最大值了,可以
维护队首最大,每次从队尾入队,并保证单调性(把小的删掉),并把队首不在t步范围内的
删掉。然后分别从左到右,从右到左这样搞一遍,答案就出来了。

#include <set>
#include <map>
#include <cmath>
#include <deque>
#include <queue>
#include <vector>
#include <string>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long Long;
const int maxn = 100 + 10;
const int maxm = 1e4 + 10;
const int inf = 0x3f3f3f3f;
int arr[maxn][maxm], sum[maxn][maxm], dp[maxn][maxm];
deque<int> Q;

int main()
{ 
    int n, m, x, t; 
    while (scanf("%d%d%d%d", &n, &m, &x, &t) != EOF)
    {
        memset(sum, 0, sizeof(sum));
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= m; j++)
            {
                scanf("%d", &arr[i][j]);
                sum[i][j] = sum[i][j-1] + arr[i][j];
            }
        }
        for (int i = 0; i <= n; i++) 
        {
            for (int j = 0; j <= m; j++) 
            {
                dp[i][j] = -inf;
            }
        }
        dp[0][x] = 0;
        for (int i = 1; i <= n; i++)
        {
            Q.clear();
            for (int j = 1; j <= m; j++)
            {
                while (!Q.empty() && dp[i-1][Q.back()]-sum[i][Q.back()-1] <= 
                    dp[i-1][j] - sum[i][j-1])
                {
                    Q.pop_back();
                }
                while (!Q.empty() && Q.front() < j-t)
                {
                    Q.pop_front();
                }
                Q.push_back(j);
                dp[i][j] = max(dp[i][j], dp[i-1][Q.front()]-sum[i][Q.front()-1] + 
                    sum[i][j]);
            }
            Q.clear();
            for (int j = m; j >= 1; j--)
            {
                while (!Q.empty() && dp[i-1][Q.back()]+sum[i][Q.back()] <= 
                    dp[i-1][j] + sum[i][j])
                {
                    Q.pop_back();
                }
                while (!Q.empty() && Q.front() > j+t)
                {
                    Q.pop_front();
                }
                Q.push_back(j);
                dp[i][j] = max(dp[i][j], dp[i-1][Q.front()]+sum[i][Q.front()] - 
                    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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值