HDU 2490 Parade(DPの单调队列)(2008 Asia Regional Beijing)

Description

Panagola, The Lord of city F likes to parade very much. He always inspects his city in his car and enjoys the welcome of his citizens. City F has a regular road system. It looks like a matrix with n+1 west-east roads and m+1 north-south roads. Of course, there are (n+1)×(m+1) road crosses in that system. The parade can start at any cross in the southernmost road and end at any cross in the northernmost road. Panagola will never travel from north to south or pass a cross more than once. Citizens will see Panagola along the sides of every west-east road. People who love Panagola will give him a warm welcome and those who hate him will throw eggs and tomatoes instead. We call a road segment connecting two adjacent crosses in a west-east road a “love-hate zone”. Obviously there are m love-hate zones in every west-east road. When passing a love-hate zone, Panagola may get happier or less happy, depending on how many people love him or hate him in that zone. So we can give every love-hate zone a “welcome value” which may be negative, zero or positive. As his secretary, you must make Panagola as happy as possible. So you have to find out the best route ----- of which the sum of the welcome values is maximal. You decide where to start the parade and where to end it.  

When seeing his Citizens, Panagola always waves his hands. He may get tired and need a break. So please never make Panagola travel in a same west-east road for more than k minutes. If it takes p minutes to pass a love-hate zone, we say the length of that love-hate zone is p. Of course you know every love-hate zone’s length.  

The figure below illustrates the case in sample input. In this figure, a best route is marked by thicker lines.  
HDU 2490 Parade(DPの单调队列)(2008 Asia Regional Beijing)
 

Input

There are multiple test cases. Input ends with a line containing three zeros.   Each test case consists of 2×n + 3 lines.  
The first line contains three integers: n, m and k.(0<n<=100,0<m<=10000, 0<=k<=3000000)  
The next n+1 lines stands for n + 1 west-east roads in north to south order. Each line contains m integers showing the welcome values of the road’s m love-hate zones, in west to east order.  
The last n+1 lines also stands for n + 1 west-east roads in north to south order. Each line contains m integers showing the lengths (in minutes) of the road's m love-hate zones, in west to east order.  
 

Output

For each test case, output the sum of welcome values of the best route. The answer can be fit in a 32 bits integer.

 

 题目大意:有一个n*m的矩阵,只能沿着边走,只能往左、往右或往上走,在同一行只能沿一个方向走(走了左边就不能返回走右边了)。打横的边都有一个权值(可能为负数)和一个长度,每行走过的长度不能超过k,打竖的边没有权值和长度。先要从最下面的任意一个点开始,走到最上面的任意一个点,问最大权值和为多少(答案不超过2^32-1,虽然题目不是这么说的)。

思路:一看就是动态规划,每一行只和上一行的状态有关。因为习惯从小到大循环我们从上往下走,反正都一样。设dp[i][j]为走到第 i 行第 j 个点的最大权值(已往左往右走完),那么dp[i][j] = max(dp[i-1][x] + sum(welcome[i][y])),distance(x, y) ≤ k,y in [x, i]。其中distance和sum(welcome[i][y])可以预处理出来(如sum[i]代表1~i的和,distance(i, j) = sum[j] - sum[i],i ≤ j),平均到处理每个dp[i][j]身上时间复杂度为O(1)。但是这样计算dp数组,时间复杂度高达O(nm²)。

现假设我们从左到右走,那么dp[i][j] = max(dp[i - 1][x] - sum_welcome[x] + sum_welcome[y]) = dp[i][j] = max(dp[i - 1][x] - sum_welcome[x]) + sum_welcome[y],那么对每一个j,所用的dp[i - 1][x] - sum_welcome[x]都是一样的,这里很容易能想到单调队列优化(如果你知道单调队列的话)。每次把队列末尾小于dp[i - 1][j] - sum_welcome[j]弹出,把队头distance(i, x) > k的弹出,队头就是最佳的dp[i - 1][x] - sum_welcome[x]。优化完时间复杂度为O(nm),已经是读入数据的复杂度了。(这里不介绍单调队列)

PS:可恶这题居然不让人在线非要我把整个矩阵一起读进来……

 

代码(1078MS,可恶啊C++又比G++快一倍):

 1 #include <cstdio>

 2 #include <cstring>

 3 #include <iostream>

 4 #include <algorithm>

 5 using namespace std;

 6 

 7 const int MAXN = 110;

 8 const int MAXM = 10010;

 9 

10 int wel[MAXN][MAXM], len[MAXN][MAXM];

11 int sum_w[MAXM], sum_l[MAXM];

12 int a[MAXM], b[MAXM], head, tail;

13 int dp[2][MAXM];

14 int n, m, k, cur;

15 

16 inline void insert(int x, int y) {

17     while(head != tail && a[tail - 1] < x) --tail;

18     a[tail] = x; b[tail] = y; ++tail;

19 }

20 

21 void solve() {

22     memset(dp, 0, sizeof(dp));

23     cur = 0;

24     for(int i = 0; i < n; ++i) {

25         cur ^= 1;

26         memset(dp[cur], 0, sizeof(dp[cur]));

27 

28         sum_w[0] = sum_l[0] = 0;

29         for(int j = 1; j <= m; ++j) sum_w[j] = sum_w[j - 1] + wel[i][j];

30         for(int j = 1; j <= m; ++j) sum_l[j] = sum_l[j - 1] + len[i][j];

31         head = tail = 0;

32         for(int j = 0; j <= m; ++j) {

33             insert(dp[cur ^ 1][j] - sum_w[j], sum_l[j]);

34             while(k < sum_l[j] - b[head]) ++head;

35             dp[cur][j] = max(dp[cur][j], a[head] + sum_w[j]);

36         }

37 

38         sum_w[m] = sum_l[m] = 0;

39         for(int j = m; j > 0; --j) sum_w[j - 1] = sum_w[j] + wel[i][j];

40         for(int j = m; j > 0; --j) sum_l[j - 1] = sum_l[j] + len[i][j];

41         head = tail = 0;

42         for(int j = m; j >= 0; --j) {

43             insert(dp[cur ^ 1][j] - sum_w[j], sum_l[j]);

44             while(k < sum_l[j] - b[head]) ++head;

45             dp[cur][j] = max(dp[cur][j], a[head] + sum_w[j]);

46         }

47     }

48 }

49 

50 int main() {

51     while(scanf("%d%d%d", &n, &m, &k) != EOF) {

52         if(n == 0 && m == 0 && k == 0) break;

53         ++n;

54         for(int i = 0; i < n; ++i)

55             for(int j = 1; j <= m; ++j) scanf("%d", &wel[i][j]);

56         for(int i = 0; i < n; ++i)

57             for(int j = 1; j <= m; ++j) scanf("%d", &len[i][j]);

58         solve();

59         int ans = 0;

60         for(int i = 0; i <= m; ++i) ans = max(ans, dp[cur][i]);

61         printf("%d\n", ans);

62     }

63 }
View Code

 

你可能感兴趣的:(2008)