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

信奥赛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;
}
相关推荐
Titan20242 小时前
Linux动静态库
linux·服务器·c++
j_xxx404_3 小时前
MySQL表操作硬核解析:从 CREATE TABLE 到磁盘文件、ALTER TABLE 与 DDL 风险
运维·服务器·数据库·c++·mysql·adb·ai
wuminyu3 小时前
Java锁机制之park和unpark源码剖析
java·linux·c语言·jvm·c++
玖玥拾4 小时前
C/C++ 基础笔记(十一)类的进阶
c语言·c++·设计模式·
-森屿安年-4 小时前
1137. 第 N 个泰波那契数
c++·动态规划
程序员老舅5 小时前
从内核视角,看Linux文件读写过程
linux·服务器·c++·内核·linux内核·vfs·linux内存
Soari5 小时前
llama.cpp更新(b9553):LLM inference in C/C++,本地和云端实现高性能大模型推理
c语言·c++·llama
2601_961194025 小时前
考研资料电子版|去哪找|网盘
java·c语言·c++·python·考研·php
Peter·Pan爱编程5 小时前
23. 算法库:用算法代替手写循环
c++·人工智能·算法