信奥赛C++提高组csp-s之搜索进阶(记忆化搜索案例实践3)

方格取数
题目描述
设有 n × m n \times m n×m 的方格图,每个方格中都有一个整数。现有一只小熊,想从图的左上角走到右下角,每一步只能向上、向下或向右走一格,并且不能重复经过已经走过的方格,也不能走出边界。小熊会取走所有经过的方格中的整数,求它能取到的整数之和的最大值。
输入格式
第一行有两个整数 n , m n, m n,m。
接下来 n n n 行每行 m m m 个整数,依次代表每个方格中的整数。
输出格式
一个整数,表示小熊能取到的整数之和的最大值。
输入输出样例 1
输入 1
3 4
1 -1 3 2
2 -1 4 -1
-2 2 -3 -1
输出 1
9
输入输出样例 2
输入 2
2 5
-1 -1 -3 -2 -7
-2 -1 -4 -1 -2
输出 2
-10
说明/提示
样例 1 解释

样例 2 解释

数据规模与约定
- 对于 20 % 20\% 20% 的数据, n , m ≤ 5 n, m \le 5 n,m≤5。
- 对于 40 % 40\% 40% 的数据, n , m ≤ 50 n, m \le 50 n,m≤50。
- 对于 70 % 70\% 70% 的数据, n , m ≤ 300 n, m \le 300 n,m≤300。
- 对于 100 % 100\% 100% 的数据, 1 ≤ n , m ≤ 10 3 1 \le n,m \le 10^3 1≤n,m≤103。方格中整数的绝对值不超过 10 4 10^4 104。
思路分析
题目大意:
在 n×m 的方格图中,每个方格有一个整数。小熊从左上角 (1,1) 走到右下角 (n,m),每一步可以向上、向下或向右走一格,不能重复经过已经走过的方格,求经过的整数和的最大值。
分析思路:
这道题的特殊性在于可以向上、向下、向右走,但不能向左走。这意味着对于某个点 (x, y),小熊可能是从下面来的,也可能是从上面来的,路径走向直接影响后续可达状态。
因此需要在状态中加入方向信息:
- 设
f[x][y][k]表示到达(x, y)时能取到的最大整数和 k = 0:从下方来到该点k = 1:从上方来到该点- 从左边来不需要单独标识,因为向左走被禁止
采用逆向搜索更简便:从 (n, m) 向 (1, 1) 搜索,递归考虑每一步的来源方向。
代码实现
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1005;
const ll INF = 2e18;
int n, m;
ll a[N][N]; // 存储方格中的数值
ll f[N][N][2]; // f[x][y][k]:到达(x,y)时的最大值,k=0从下方来,k=1从上方来
ll dfs(int x, int y, int k) {
// 边界条件:出界返回负无穷
if (x < 1 || x > n || y < 1 || y > m) return -INF;
// 起点:只有(1,1)才能直接返回
if (x == 1 && y == 1) return a[1][1];
// 记忆化:已经计算过直接返回
if (f[x][y][k] != -INF) return f[x][y][k];
ll ans = -INF;
// 从左边来(不管k是0还是1,都可以从左边来)
ans = max(ans, dfs(x, y - 1, 0)); // 左边点不管从哪来
ans = max(ans, dfs(x, y - 1, 1));
// 根据k决定是否可以从上方或下方来
if (k == 0) { // 当前点是从下方来的,那么上一个点只能在它的下方(不能往上,因为不能折返)
ans = max(ans, dfs(x + 1, y, 0)); // 从下方来
} else { // k == 1,当前点是从上方来的
ans = max(ans, dfs(x - 1, y, 1)); // 从上方来
}
// 加上当前点的数值,存入备忘录并返回
return f[x][y][k] = ans + a[x][y];
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> a[i][j];
}
}
// 初始化备忘录为负无穷
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
f[i][j][0] = f[i][j][1] = -INF;
}
}
// 从(n, m)出发,终点(n,m)只能从上方或左边来,不能从下方来
// 所以分别计算k=0和k=1两种情况,取最大值
ll ans = max(dfs(n, m, 0), dfs(n, m, 1));
cout << ans << endl;
return 0;
}
功能分析
| 模块 | 功能说明 |
|---|---|
三维备忘录 f[N][N][2] |
存储每个位置在不同到达方向下的最大路径和,用-INF表示未计算 |
方向参数 k |
区分从下方(0)还是从上方(1)到达当前点,防止路径折返重复 |
| 逆向DFS | 从 (n, m) 往 (1, 1) 搜索,利用"从左边来"不依赖方向的性质简化状态转移 |
| 边界处理 | 出界返回 -INF(不可能路径),起点返回自身数值 |
| 取最大值 | 综合左边、上方、下方三个方向的来源,选择最大路径 |
正确性保证 :状态增加方向维度后,路径不会重复经过同一个方格,保证了无后效性,记忆化确保每个 (x, y, k) 只计算一次,时间复杂度 O(n×m×2) = O(nm)。
更多系列知识,请查看专栏:《信奥赛C++提高组csp-s知识详解及案例实践》:
https://blog.csdn.net/weixin_66461496/category_13113932.html
各种学习资料,助力大家一站式学习和提升!!!
cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
cout<<"########## 一站式掌握信奥赛知识! ##########";
cout<<"############# 冲刺信奥赛拿奖! #############";
cout<<"###### 课程购买后永久学习,不受限制! ######";
return 0;
}
1、csp信奥赛高频考点知识详解及案例实践:
CSP信奥赛C++动态规划:
https://blog.csdn.net/weixin_66461496/category_13096895.html点击跳转
CSP信奥赛C++标准模板库STL:
https://blog.csdn.net/weixin_66461496/category_13108077.html 点击跳转
信奥赛C++提高组csp-s知识详解及案例实践:
https://blog.csdn.net/weixin_66461496/category_13113932.html
2、csp信奥赛冲刺一等奖有效刷题题解:
信奥赛C++普及组csp-j初赛&复赛真题题解(持续更新) https://blog.csdn.net/weixin_66461496/category_12808781.html 点击跳转
信奥赛C++提高组csp-s初赛&复赛真题题解(持续更新)
https://blog.csdn.net/weixin_66461496/category_13125089.html
3、GESP C++考级真题题解:

GESP(C++ 一级+二级+三级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12858102.html 点击跳转

GESP(C++ 四级+五级+六级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12869848.html 点击跳转

GESP(C++ 七级+八级)真题题解(持续更新):
https://blog.csdn.net/weixin_66461496/category_13117178.html
4、csp/信奥赛C++,完整信奥赛系列课程(永久学习):
https://edu.csdn.net/lecturer/7901 点击跳转
· 文末祝福 ·
cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
cout<<"跟着王老师一起学习信奥赛C++";
cout<<" 成就更好的自己! ";
cout<<" csp信奥赛一等奖属于你! ";
return 0;
}