搜索
深度优先搜索 DFS
采用递归实现,需要辅助数组,记录路径时需要用数组或栈。
9.3A Knight’s Journey
用深度优先算法DFS判断是否有解的问题。
#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
- 初筛选:棍长之和除以4,没有余数则可凑,有则不可凑结束。
- 深度递归筛选:上一步得正方形边长,不断取出棍,检查能否加入当前边,直到凑出三个边长。
#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");
}
}
}