当前位置: 首页 > news >正文

ch07 题目参考思路

ch07 - 深度优先搜索

滑雪

  • 知识点:路径问题、最优化问题、记忆化搜索

  • 思路:

    • 本题是路径问题,简单的想法是 dfs 搜索所有合法路径,最长的就是答案。但一个网格中的路径数量是指数级的,这样做会超时。
    • 走到一个位置 (x, y) 时,实际上只关心 (x, y) 往后还能走多长,而不是对于所有到达 (x, y) 的路径,都要继续往后搜索所有走法。
    • dfs(x, y):从 (x, y) 出发,往后最多还能走多长。
      • 下一步有 4 种选择,枚举 (x, y) 下一步能到达的位置 (cx, cy),所有选择对应的结果取最大值就是 dfs(x, y) 的结果。
      • 要计算 ( x , y ) → ( c x , c y ) → ⋯ (x, y)\to (cx, cy)\to \cdots (x,y)(cx,cy) 的最大长度,首先有 ( x , y ) (x, y) (x,y) 这一个位置,再调用 dfs(cx, cy) 得到 ( c x , c y ) → ⋯ (cx,cy)\to \cdots (cx,cy) 的最大长度,所以这种选择对应的结果是 1 + dfs(cx, cy) 。
    • 用记忆化搜索,f[x][y] 记录 dfs(x, y) 的结果,只要 f[x][y] 已经算过了,再次搜索到 dfs(x, y) 时可以直接返回结果。
    • 每一个状态 (x, y) 只需要被计算一次,时间复杂度 O ( R C ) O(RC) O(RC)
  • 代码:

    int dfs(int x, int y) {
    	if (f[x][y]) return f[x][y];
    	f[x][y] = 1; // 从(x,y)出发,最少也有它本身一个位置的长度
    	for (int i = 0; i < 4; i++) {
    		int cx = x + dx[i], cy = y + dy[i];
    		if ((x, y)走到(cx,cy)不合法) continue;
    		f[x][y] = max(f[x][y], dfs(cx, cy) + 1); // 从不同走法中取最长的
    	}
    	return f[x][y];
    }
    
  • 易错点:注意最长的路径不一定是从左上角出发的,根据 dfs(x, y) 的功能,思考 main() 函数怎么写才能得到正确答案。

水桶装水

  • 知识点:DFS 解决判断问题、状态标记优化

  • 思路:

    • dfs(a, b):当前第一个桶的水量是 a,第二个桶的水量是 b,能否达成目标。
    • 只要 (a, b) 的后继状态中有一个能达成目标,那么 (a, b) 也能达成目标。根据题目的操作,列举所有后继状态:
      • 例如装满第一个桶,那么后继状态是 (x, b)。
      • 例如要把第一个桶的水倒入第二个桶,先计算要倒过去的水量 d,取决于第一个桶“能倒出多少水”,以及第二个桶“能接收多少水”,d = min(a, y - b),后继状态是 (a - d, b + d) 。
      • 分别对两个水桶执行三种操作,总共有六种后继状态。
    • 水倒来倒去,有可能多次搜索到同一个状态,需要用数组标记搜索过的状态。
  • 代码:

    bool dfs(int a, int b) {
        if (a + b == z) return true;
        if (vis[a][b]) return false;
        vis[a][b] = true;
        // 第一个桶往第二个桶倒水,转移水量d1。第二个桶往第一个桶倒水,转移水量d2。
        int d1 = min(a, y - b), d2 = min(b, x - a);
        // 思考如何列举六种后继状态并返回结果
    }
    

Mother’s Milk

  • 知识点:DFS 解决判断问题、状态标记优化

  • 思路:

    • 需要搜索的状态比较明显是三个桶当前各自的牛奶量。
    • 在搜索过程中,用数组将能达到的状态标记为 true,最后枚举 a 桶为空的所有状态,对于能达到的状态,从小到大输出 c 桶所剩量即可。
    • dfs 过程中,从哪个桶倒到哪个桶有多种可能,手动列举比较麻烦,也容易写错,可以两层循环枚举。
  • 代码:

    bool f[N][N][N];
    int c[3], a[3]; // c[i]表示第i个桶的容量,a[i]表示当前状态第i个桶的牛奶量
    // 从第i个桶倒到第j个桶
    void pour(int i, int j) {
    	int d = min(a[i], c[j] - a[j]);
    	a[i] -= d;
    	a[j] += d;
    }
    // 当前搜索状态是(a[0], a[1], a[2])
    void dfs() {
    	if (f[a[0]][a[1]][a[2]]) return ; // 记忆化
    	f[a[0]][a[1]][a[2]] = true;
    	int b[3];
    	memcpy(b, a, sizeof(a)); // b用来临时储存a数组的值
    	for (int i = 0; i < 3; i++) {
    		for (int j = 0; j < 3; j++) {
    			if (i == j) continue;
    			pour(i, j);
    			dfs();
    			memcpy(a, b, sizeof(b)); // pour()中a数组的值被修改,此处要改回来
    		}
    	}
    }
    int main() {
    	cin >> c[0] >> c[1] >> c[2];
    	a[2] = c[2];
    	dfs();
    	for (int i = 0; i <= c[2]; i++) { // c桶所剩量
    		for (int j = 0; j <= c[2] - i; j++) { // b桶所剩量
    			if (f[0][j][i]) {
    				cout << i << " ";
    				break;
    			}
    		}
    	}
    	return 0;
    }
    

Cows on Skates

  • 知识点:网格路径问题、DFS 搜索优化

  • 思路:

    • 本题属于需要记录路径的网格路径问题,但网格规模较大,路径数量太多,罗列所有路径会超时。
    • 优化1:题目中路径可以走重复位置,但仍然可以限制同一条路径不允许走重复位置,因为对于“走到终点”这个目标来说,绕圈是没有意义的。
    • 优化2:如果搜索过一个位置 (x, y) ,这个位置无法到终点,那么其他路径也无需再尝试这个位置了。
      • 因为 (x, y) 无法到终点分为 2 种情况:一是被障碍挡住,那么显然这个位置没有前途,其他路径也无需走这里;二是被路径中的其他位置 (x1, y1) 挡住,那么 (x1, y1) 比 (x, y) 更接近终点,应该回退到 (x1, y1) 去搜索,其他路径也无需搜索这个比 (x1, y1) 更劣的位置。
      • 所以不是用 inPath[x][y] 标记 (x, y) 有没有在当前路径中,可以用 vis[x][y] 标记有没有搜索过 (x, y) 这个位置,这样可以保证每个位置只被搜索一次。
  • 代码:

    char ch[N][N]; // 输入的迷宫
    int n, m; // 迷宫的行数和列数
    struct P{
    	int x, y;
    } path[N*N]; // 记录状态(路径)的数组
    bool vis[N][N]; // vis[x][y]标记(x,y)这个位置有没有搜索过
    // (x,y)是当前状态(路径)的末尾位置,len是当前路径的长度
    void dfs(int x, int y, int len) {
        if (vis[x][y]) return ;
        vis[x][y] = true;
    	if (x == n && y == m) {
    		// 输出结果
    		return ;
    	}
    	for (int i = 0; i < 4; i++) {
    		int cx = x + dx[i], cy = y + dy[i];
    		if (cx < 1 || cx > n || cy < 1 || cy > m || ch[cx][cy] != '.') continue;
    		path[len+1] = {cx, cy}; // 新位置放入路径中
    		dfs(cx, cy, len + 1);
    	}
    }
    int main() {
        // 省略输入
    	path[1] = {1, 1}; // 注意起点放入路径中
    	dfs(1, 1, 1);
    	return 0;
    }
    

Cow Travelling

  • 知识点:路径问题、记忆化搜索、基本计数原理

  • 思路:

    • 与”滑雪“类似,不能去罗列所有路径,会超时。要有将原问题分解为子问题解决的思维,重复的子问题通过记忆化搜索避免重复计算,以此提高效率。
    • 关键信息是位置和时间,那么状态中应该包含位置和时间。
    • dfs(x, y, t):从 (x, y) 位置出发,经过 t 秒到达终点的路径方案数。
    • 考虑下一步到达的位置 (cx, cy),有上下左右四种走法,分为四类情况统计从 (x, y) 出发的路径方案数:
      • 从 (x, y) 走到 (cx, cy) 用掉了 1 秒,那么只剩下 t - 1 秒从 (cx, cy) 走到终点,调用 dfs(cx, cy, t - 1) 得到这一类走法的方案数。
      • 分类统计,应用加法原理。
    • 花同样的时间,走不同路径,是有可能到达同一个位置的,所以会重复搜索到同一个状态,需要记忆化搜索。
    • 答案的范围:因为题目输入的时间 T 最大是 15,每一步最多有 4 种走法选择,最多走 15 步,根据乘法原理,路径方案数不会超过 4 15 4^{15} 415 ,在 int 范围内。
  • 代码:

    int dfs(int x, int y, int t) {
    	if (t == 0) { // 时间用完了,到达终点说明是一条满足要求的路径,方案数为1,没到终点则是0
    		return (x == r2 && y == c2 ? 1 : 0);
    	}
    	if (f[x][y][t] != -1) return f[x][y][t]; // 方案数不会是-1,用-1作为f的初始值表示没搜索到这个状态
    	f[x][y][t] = 0;
    	for (int i = 0; i < 4; i++) {
    		int cx = x + dx[i], cy = y + dy[i];
    		if (cx < 1 || cx > n || cy < 1 || cy > m || ch[cx][cy] == '*') continue;
    		f[x][y][t] += dfs(cx, cy, t - 1); // 分为往上下左右四类走法,分类用加法原理
    	}
    	return f[x][y][t];
    }
    
  • 拓展:

    • 为什么 dfs(x, y, t) 要定义为从 (x, y) 位置出发,经过 t 秒到达终点的路径方案数?
    • 能不能定义为从起点出发,经过 t 秒到达 (x, y) 位置的方案数?思考并尝试。

相关文章:

  • Git 实践笔记
  • 【远程工具】0 std::process::Command 介绍
  • 一周学会Pandas2 Python数据处理与分析-Pandas2读取Excel
  • 30天学Java第九天——线程
  • 计算机网络- 传输层安全性
  • BUG:Cannot find implementation for xxx. database. xxx. xxx_Impl does not exist
  • 2024第十五届蓝桥杯大赛软件赛省赛Java大学B组 报数游戏 类斐波那契循环数 分布式队列 食堂 最优分组 星际旅行 LITS游戏 拼十字
  • 【力扣hot100题】(089)最长有效括号
  • 通用 Web 项目安全加固 Checklist(语言无关通用模板)
  • 【2025年认证杯数学中国数学建模网络挑战赛】A题 解题建模过程与模型代码(基于matlab)
  • ch07 部分题目思路
  • 量子指纹识别
  • 【数据结构】排序
  • golang通过STMP协议发送邮件功能详细操作
  • 化工行业电气智能化管理系统解决方案
  • CVE-2025-31486 Vite开发服务器任意文件读取漏洞复现
  • Pytorch实现基于FlowS-Unet的遥感图像建筑物变化检测方法
  • wireshark抓包,镜像端口,观察端口
  • protobuf的应用
  • 第三节:React 基础篇-React组件通信方案
  • 东莞加盟网站建设/公司软文
  • 有一个做搞笑英语视频网站/天津seo排名
  • 做废品回收哪个网站好点/百度账号登录入口网页版
  • 给我一个网站图片/智能搜索引擎
  • 房产经济人怎么做网站/长沙百度
  • 百度网站怎么做视频播放器/武汉网站推广公司