王道复试C语言 第八章搜索(下:深度优先搜索DFS)——代码笔记分享

搜索

深度优先搜索 DFS

采用递归实现,需要辅助数组,记录路径时需要用数组或栈
在这里插入图片描述

9.3A Knight’s Journey

用深度优先算法DFS判断是否有解的问题。
在这里插入图片描述

北大OJ

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string>
using namespace std;
/*
题目:
骑士走日(即一步可以向斜上下左右两格方向移动,共8个方向),
棋盘纵轴顺序为A,B,C...,横轴顺序为1,2,3...,
在棋盘中找到一条可以遍历棋盘每一个格子的路径。
输入:
第一行输入n,代表有n种棋盘,
往后n行每行输入p和q(1 <= p*q <= 26),代表横向有p块,纵向有q块。
输出:
每个输出以"Scenario #i"开头,
再输出一个路径即走过的格子集合,需按字典顺序输出,
没有路径则输出"impossible"。
*/

int direction[8][2] = {//8个方向
	//按字典顺序输出,则先让y值减小,再让x值减小
	{-1,-2},{1,-2},{-2,-1},{2,-1},{-2,1},{2,1},{-1,2},{1,2}
};
bool isvisit[50][50];
bool DFS(int x, int y, int cur, int p, int q, string path) {
	//x当前格子横轴,y当前格子纵轴,cur当前走过的格子数,p棋盘横向数,q棋盘纵向数,path路径即走过的点集合
	//把本节点加入路径
	path += (y + 'A');//棋盘纵轴顺序为A,B,C...
	path += (x + '1');//棋盘横轴顺序为1,2,3...
	//将当前节点设为已访问
	isvisit[x][y] = true;
	//判断是否达到终点:走过的格子数==棋盘数
	if (cur == p * q) {
		printf("%s\n\n", path.c_str());//打印路径
		return true;
	}
	//没走到终点,则遍历邻居
	for (int i = 0; i < 8; i++) {//8个方向均遍历一遍
		if (x + direction[i][0] >= 0 && x + direction[i][0] < p &&
			y + direction[i][1] >= 0 && y + direction[i][1] < p &&
			isvisit[x + direction[i][0]][y + direction[i][1]] == false
			) {
			if (DFS(x + direction[i][0], y + direction[i][1], cur + 1, p, q, path) == true) {//若邻居可以达到终点
				//则此步也可以达到终点
				return true;
			}
		}
	}

	//遍历完邻居都没有返回,则不存在从本节点出发到终点的路径
	isvisit[x][y] = false;//当前节点恢复未访问
	path.substr(0, path.size() - 2);//恢复路径,把最后两个字符去掉
	return false;
}

int main() {
	int n, p, q;
	scanf("%d", &n);
	for (int idx = 0; idx < n; idx++) {
		scanf("%d%d", &p, &q);
		printf("Scenario #%d\n", idx + 1);
		//每次棋盘遍历前将都将访问数组初始化
		for (int i = 0; i < p; i++) {
			for (int j = 0; j < q; j++) {
				isvisit[i][j] = false;
			}
		}
		string path;
		if (!DFS(0, 0, 1, p, q, path)) {
			printf("impossible\n\n");
		}
	}
}

9.4Square

  1. 初筛选:棍长之和除以4,没有余数则可凑,有则不可凑结束。
  2. 深度递归筛选:上一步得正方形边长,不断取出棍,检查能否加入当前边,直到凑出三个边长。
    在这里插入图片描述

北大OJ

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
using namespace std;
/*
题目:
给你一些长度不同的棍子,是否有可能把他们连接在一起形成一个正方形?
输入:
第一行为n,代表有n组棍子,
之后n行,每行第一个数字m(4 <= m <= 20)为此组棍子数,此后m个数字为每个棍子长度(1 to 10,000)。
输出:
输出
*/

int m;//全局变量,此组m个棍子
int stick[30];//全局变量,存储棍子长度
bool isused[30];//全局变量,存储棍子是否已被选中

bool DFS(int curSide, int curLength, int position, int side) {
	//curSide 已经拼好的边长的个数
	//curLength 正在拼的边长的长度
	//position 木棍遍历的起点
	//side 正方形边长

	//凑够三条边则成功
	if (curSide == 3) {
		return true;
	}

	//从前往后,从当前棍子开始选直到最后一个
	for (int i = position; i < m; i++) {
		//判断当前棍子是否可用
		//若当前棍子已选,或当前长度+当前棍长后超出边长,则当前棍子不可用
		if (isused[i] == true || curLength + stick[i] > side) {
			continue;
		}

		//当前棍子可用,暂时用其拼边
		isused[i] = true;//置为已选
		if (curLength + stick[i] < side) {//加入后长度不足边长
			//继续遍历后一根棍子
			if (DFS(curSide, curLength + stick[i], i + 1, side)) {
				return true;
			}
		}
		else if (curLength + stick[i] == side) {//加入后拼出边长
			//已拼好的边长数加1,其他初始化再继续递归拼边
			if (DFS(curSide + 1, 0, 0, side)) {
				return true;
			}
		}

		//当前木棍不满足,取回
		isused[i] = false;	
	}
	//拼边失败
	return false;
}

int main() {
	int n;
	scanf("%d", &n);
	for (int idx = 0; idx < n; idx++) {
		scanf("%d", &m);

		int total = 0;//棍子总长度
		for (int i = 0; i < m; i++) {
			scanf("%d", &stick[i]);
			total += stick[i];
			isused[i] = false;
		}
		int side;//正方形边长
		if (total % 4 == 0) {//初筛选
			side = total / 4;
			if (DFS(0, 0, 0, side)) {//深度递归筛选为真,则拼边成功
				printf("yes\n");
			}
			else {
				printf("no\n");
			}
		}
		else {
			printf("no\n");
		}
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值