第九章 搜索01
如果求最低话费的路径,则四要素转变为![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/de33a0d20be48dead72d8b8799f6099b.png)
搜索问题:起始状态经过一系列的状态转移抵达目标状态
Search Trees–宽度优先搜索(BFS)& 深度优先搜索(DFS)
宽度优先搜索(BFS)-- 队列
例题王道9.1:
分析:
状态空间(位置n,时间t)
状态转移 (n-1.t+1),(n+1,t+1),(n*2,t+1)
起始状态(N,0)
目标状态(K ,lowest time)
分析:
- 使用宽度优先搜索,
- 每一层是相同的时间 并且为防止访问同样的点浪费程序时间:可以设置一个visit数组
#include <iostream>
#include <cstdio>
//因为使用的是宽度优先搜索,所以需要用到队列这个数据结构,所以需要加上:
#include <queue>
//memset使用需要加上
#include <cstring>
using namespace std;
//题目要求的是100000
const int MAXN = 1e5 + 10;
//需要用一个结构体来存储状态,注意结构体后面要加分号
struct Status {
int position;
int time;
Status(){}
Status(int p,int t):position(p), time(t){}
};
//定义一个访问数组
bool visit[MAXN];
//写一个宽度优先搜索的函数
int BFS(int n, int k) {
//定义一个存放状态的队列
queue<Status> myQueue;
myQueue.push(Status(n,0)); //注意压入队列的写法
visit[n] = true;
//如果队列非空就
while(!myQueue.empty()) {
Status current = myQueue.front();
if(current.position == k){
return current.time;
}
//除此之外就是没找到的状态
myQueue.pop();
//转入新的状态,使用状态转换方程
for(int i = 0; i < 3; i++){
Status next = current;
if(i == 0) {
next.position -= 1;
} else if(i == 1){
next.position += 1;
} else {
next.position *= 2;
}
next.time += 1;
//如果下一个位置不合法,则跳过(不对这个状态进行扩展);为了提高搜索性能
if(next.position < 0 || next.position > 1e5 || visit[next.position]){
continue;
}
//若合法 或者 没有访问过的状态,则压入队列
myQueue.push(next);
visit[next.position] = true;
}
}
}
int main(){
int n,k;
scanf("%d%d",&n,&k);
memset(visit, false, sizeof(visit));
//主函数中调用这个函数即可
printf("%d\n", BFS(n,k));
return 0;
}
例题9。2:
分析:
状态空间:n*k
状态转移:n*k + n
起始状态: n
目标状态:n * k 由 0,1构成
上诉方法的状态空间过大,可以反过来思考:找01构成的数,反过来看是否是n的倍数,效率高很多
状态空间:01构成的number
状态转移:(number*10),(number*10 + 1)
起始状态:1
目标状态:number % n == 0
Search tree描述:
由于不会出现重复的节点数值,所以不需要设置visit数组
BFS
代码:
#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
//定义BFS
void BFS(int n){
queue<long long> myQueue; //队列的声明,尖括号中是数据类型
myQueue.push(1);
while(!myQueue.empty()) {
long long current = myQueue.front();
if(current % n == 0){
printf("%lld\n",current); //long long输出需要lld!!!!!
return;
}
myQueue.pop();
/*转入新的状态,使用状态转换方程:
1.使用循环,有几个转换方程循环几次,用if(i==循环次数)进行状态转移
2.考虑是否合法或有无访问过的状态
3。压入队列
*/
for(int i = 0; i < 2; i++){
if(i == 0){
myQueue.push(current * 10);
}
if(i == 1){
myQueue.push(current * 10 + 1);
}
}
}
}
int main(){
int n;
while (scanf("%d",&n) != EOF){
if(n == 0){
break;
}
BFS(n);
}
return 0;
}
遇到的错误点:wrong answer --输出long long 需要用到%lld
第九章 搜索02
深度优先搜索DFS – 栈 或 递归的方式不用栈
例题9.3:题目
注意:
1.因为层数==遍历走过的格子数,所以优先深度遍历
2.根据题意,需要设置visit数组,如果访问过,则下次则不能访问
老师讲得不详细,这个博客有详细:别人的解释
顺序:
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
using namespace std;
//骑士可以前行的8个方向用二维数组存储.按照字典从小到大
int direction[8][2] = {
{-1,-2}, {1,-2},{-2,-1},{2,-1},{-2,1},{2,1},{-1,2},{1,2}
};
const int MAXN = 30;
//棋盘参数
int p,q;
//定义visit数组,二维数组
bool visit[MAXN][MAXN];
//深度优先搜索,返回bool值:能否找到一条能遍历完所有棋盘的路径
bool DFS(int x, int y, int step,string ans) {
if(step == p * q) { //搜索成功
cout << ans << endl << endl;
return true;
} else {
for(int i = 0; i < 8;i++) { //遍历邻居节点
int nx = x + direction[i][0]; //扩展状态坐标
int ny = y + direction[i][1];
char col = ny + 'A'; //该点编号
char row = nx + '1';
if(nx < 0 || nx >= p || ny < 0 || ny >= q || visit[nx][ny]) {
continue;
}
visit[nx][ny] = true; //标记该点
if(DFS(nx,ny,step+1,ans+col+row)) {
return true;
}
visit[nx][ny] = false; //取消标记
}
}
return false;
}
int main(){
int n;
scanf("%d", &n);
int caseNumber = 0;
while (n--){
scanf("%d%d",&p,&q);
memset(visit,false,sizeof(visit));
cout << "Scenario #" << ++caseNumber << ":" << endl;
visit[0][0] = true; //标记A1点
if(!DFS(0,0,1,"A1")) {
cout << "impossible" << endl << endl;
}
}
return 0;
}
阅读算法笔记p268,多读书
计算机常识:
数据结构和操作系统中的栈,堆的区别