动态规划+单调队列
记左上角的十字路口为(1, 1), 右下角的十字路口为 (n + 1, m + 1)
记f[i][j] 表示从第一行任意十字路口出发游行到 (i, j) 的最大收益
可以用前缀和来表示同一行上任意两点之间的欢迎值、距离之和
讨论在同一行间的移动方向得到转移方程:
f[i][j] = max(f[i - 1][j], max(f[i - 1][t] + abs(p[i][j] - p[i][t])) // abs(e[i][j] - e[i][t]) <= k
因为对于任意 (i, j) dis(j, i) <= dis(j, i + 1), 所以考虑用单调队列维护 max(f[i - 1][t] + abs(p[i][j] - p[i][t]) 的值
对于从左往右走:
若 j, k (j < k) 满足 f[i - 1][k] - p[i][k] >= f[i - 1][j] - p[i][j] 那么 k 一定比 j 优
对于从右往左走与之类似,不在讨论
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int N = 105, M = 1e4 + 5; 5 typedef long long LL; 6 7 int n, m, k, q[M], l, r; 8 LL f[N][M], e[N][M], p[N][M], ans = 0; 9 10 void dp() { 11 ans = 0; 12 for(int i = 1; i <= m; i++) f[0][i] = 0; 13 for(int i = 1; i <= n; i++) { 14 l = r = 1; 15 q[l] = 1; 16 for(int j = 1; j <= m; j++) f[i][j] = f[i - 1][j]; 17 for(int j = 2; j <= m; j++) { 18 while(e[i][j] - e[i][q[l]] > k && l <= r) l++; 19 if(l <= r) f[i][j] = max(f[i][j], f[i - 1][q[l]] + p[i][j] - p[i][q[l]]); 20 while(l <= r && f[i - 1][j] - p[i][j] >= f[i - 1][q[r]] - p[i][q[r]]) r--; 21 q[++r] = j; 22 } 23 l = r = 1; 24 q[l] = m; 25 for(int j = m - 1; j; j--) { 26 while(e[i][q[l]] - e[i][j] > k && l <= r) l++; 27 if(l <= r) f[i][j] = max(f[i][j], f[i - 1][q[l]] + p[i][q[l]] - p[i][j]); 28 while(l <= r && f[i - 1][j] + p[i][j] >= f[i - 1][q[r]] + p[i][q[r]]) r--; 29 q[++r] = j; 30 } 31 } 32 for(int i = 1; i <= m; i++) 33 ans = max(ans, f[n][i]); 34 cout<<ans<<endl; 35 } 36 37 int main() { 38 while(cin>>n>>m>>k && n) { 39 n++; m++; 40 memset(p, 0, sizeof(p)); 41 memset(e, 0, sizeof(e)); 42 memset(f, -0x7f, sizeof(f)); 43 for(int i = 1; i <= n; i++) 44 for(int j = 2; j <= m; j++) { 45 scanf("%lld", &p[i][j]); 46 p[i][j] += p[i][j - 1]; 47 } 48 for(int i = 1; i <= n; i++) 49 for(int j = 2; j <= m; j++) { 50 scanf("%lld", &e[i][j]); 51 e[i][j] += e[i][j - 1]; 52 } 53 dp(); 54 } 55 return 0; 56 }