题目大意:有n+1条横线,m+1条竖线,你的任务是从最南边的路走到最北边的路,使得走过的路上的高兴值和最大。同一段路不能走超过两次,且不能从北往南走,另外每条横向路上所花的时间不能超过k
解题思路:设dp[i][j]为走到第i条横线,第j条竖线的十字路口的高兴值的最大值
则dp[i][j] = max(dp[i][j], dp[i-1][k] + sum_value[j-1]-sum_value[j][k-1])
然后从左到右扫描更新,再从右到左扫描更新
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define maxn 110
#define maxm 10010
#define INF 0x3f3f3f3f
int sum_len[maxn][maxm], sum_value[maxn][maxm], q[maxm], dp[maxn][maxm], n, m, k;
void DP() {
int front, rear;
for(int i = 1; i <= n; i++) {
front = rear = 0;
q[0] = 1;
for(int j = 1; j <= (m+1); j++) {
while(front <= rear && dp[i-1][q[rear]] - sum_value[i][q[rear]-1] <= dp[i-1][j] - sum_value[i][j-1])
rear--;
q[++rear] = j;
while(front <= rear && sum_len[i][j-1] - sum_len[i][q[front]-1] > k)
front++;
dp[i][j] = dp[i-1][q[front]] + sum_value[i][j-1] - sum_value[i][q[front]-1];
}
front = rear = 0;
q[0] = m + 1;
for(int j = m+1; j >= 1; j--) {
while(front <= rear && dp[i-1][q[rear]] + sum_value[i][q[rear]-1] <= dp[i-1][j] + sum_value[i][j-1])
rear--;
q[++rear] = j;
while(front <= rear && sum_len[i][q[front]-1] - sum_len[i][j-1] > k)
front++;
dp[i][j] = max(dp[i][j],dp[i-1][q[front]] + sum_value[i][q[front]-1]-sum_value[i][j-1]);
}
}
}
int main() {
while(scanf("%d%d%d", &n, &m, &k) == 3 && (n+m+k)) {
n++;
int tmp;
for(int i = 1; i <= n; i++) { sum_value[i][0] = 0; for(int j = 1; j <= m; j++) {
scanf("%d", &tmp);
sum_value[i][j] = sum_value[i][j-1] + tmp;
}
}
for(int i = 1; i <= n; i++) {
sum_len[i][0] = 0;
for(int j = 1; j <= m; j++) {
scanf("%d",&tmp);
sum_len[i][j] = sum_len[i][j-1] + tmp;
}
}
for(int i = 1; i <= (m+1); i++)
dp[0][i] = 0;
DP();
int ans = -INF;
for(int i = 1; i <= (m+1); i++)
ans = max(ans,dp[n][i]);
printf("%d\n",ans);
}
return 0;
}