P7311 [COCI2018-2019#2] Maja 题解
看到本题没有题解,那就由我来写一篇吧。
首先,看到这题,我想到了一种十分暴力的 dp 做法。
令
还好
引理:Maja 一定是在一个点和与这个点相邻的权值最大的点周期性地来回地走动。
我们简单证明一下这个引理。假设 Maja 是在三个点上来回走动。这是不可能得到最优解的。因为此时,他不如选择其中权值最大的两个点进行走动,反而更优。
就比如说,这三个点分别是1,2,3,权值为
明显可以看出,周期为4,并且一个周期平均权值为
当然,我们还有两种决策,平均权值分别为
因为
所以这两者平均权值必有一个大于后者。
综上,我们证明了必须在相邻两个点来回走动。
然后还是 dp。状态类似,就是步数上限变成了
其中
计算过程中可以顺便统计一下答案,只要根据引理,取与当前这个点相邻的点中权值最大的即可。
时间复杂度
#include <bits/stdc++.h>
using namespace std;
int n, m, a[110][110], A, B;
typedef long long ll;
ll f[110][110][2], ans, K;
const int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, -1, 1};
bool valid(int x, int y) {
return x >= 1 && x <= n && y >= 1 && y <= m;
}
int main() {
scanf("%d%d%d%d%lld", &n, &m, &A, &B, &K);
for (register int i = 1; i <= n; ++i)
for (register int j = 1; j <= m; ++j) scanf("%d", a[i] + j);
bool tag = 1;
memset(f, 0xcf, sizeof f);
f[A][B][1] = 0;
for (register int k = 1; k <= min(1ll * n * m, K >> 1); ++k) {
tag ^= 1;
for (register int i = 1; i <= n; ++i)
for (register int j = 1; j <= m; ++j) {
ll maxc = 0;
for (register int p = 0; p < 4; ++p) {
int nx = i + dx[p], ny = j + dy[p];
if (!valid(nx, ny)) continue;
f[i][j][tag] = max(f[i][j][tag], f[nx][ny][tag ^ 1] + a[i][j]);
maxc = max(maxc, 1ll * a[i][j] + a[nx][ny]);
}
ans = max(ans, (f[i][j][tag]) * 2 + (K / 2 - k) * maxc - a[i][j]);
}
}
printf("%lld", ans);
}