题解 P7074 【方格取数】
看,€€£出了2000年的原题
如果本题只能向下或向右走的话,做法是显然的,就是一个数字三角形的变体。
但是这题与其它题不一样的是,小熊是可以向上走的。
我在考场上的第一反应就是将 向上、向下、向右 全部分开考虑。
但是考场上脑子短路没推出来?
结果出考场5min想到正解……
我们还是按照刚才的思路,但是简化一下:考虑只向上&向右 或 只向下&向右的最优解。
那么我们对于每一种情况,都可以轻松推出转移方程:
令f[i][j][0]表示第i行j列从上方,右方过来的最优解,f[i][j][1]表示第i行j列从下方,右方过来的最优解。
从下往上走需要倒序扫描
我们发现这里的状态只与上一列的答案有关,于是我们可以用滚动数组的技巧压掉一维。毕竟空间能省就省。
第一列只能从上往下走,亲测必须特判。而最后一列也只能从上往下走,不必特判。
#include <algorithm>
#include <cstdio>
#include <cstring>
typedef long long ll;
using namespace std;
ll read() {
register ll n = 0;
register char ch = getchar();
bool f = 1;
while (ch < '0' || ch > '9') {
if (ch == '-')
f = 0;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
n = (n << 3) + (n << 1) + (ch ^ '0');
ch = getchar();
}
return f ? n : -n;
}
const int N = 1e3 + 5;
ll f[N][2], dp[N], a[N][N];
int n, m;
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
a[i][j] = read();
// 第一列不存在像上走的情况,必须特判
dp[1] = a[1][1];
for (int i = 2; i <= n; i++)
dp[i] = dp[i - 1] + a[i][1];
for (int j = 2; j <= m; j++) {
// 因为有负数,所以需要设定边界初始值
f[1][0] = dp[1] + a[1][j], f[n][1] = dp[n] + a[n][j];
// 状态转移
for (int i = 2; i <= n; i++)
f[i][0] = max(dp[i], f[i - 1][0]) + a[i][j];
for (int i = n - 1; i >= 1; i--)
f[i][1] = max(dp[i], f[i + 1][1]) + a[i][j];
// 滚动数组
for (int i = 1; i <= n; i++)
dp[i] = max(f[i][0], f[i][1]);
}
// 最后一行是不能向上走的,只考虑向下的情况
printf("%lld\n", f[n][0]);
return 0;
}