codeup 8.2 搜索专题->广度优先搜索(BFS)

版权声明:原创文章,转载请注明出处 https://blog.csdn.net/hza419763578/article/details/88606154

问题 A: Jugs

时间限制: 1 Sec  内存限制: 32 MB
提交: 170  解决: 0
[提交][状态][讨论版][命题人:外部导入]

题目描述

In the movie "Die Hard 3", Bruce Willis and Samuel L. Jackson were confronted with the following puzzle. They were given a 3-gallon jug and a 5-gallon jug and were asked to fill the 5-gallon jug with exactly 4 gallons. This problem generalizes that puzzle.

    You have two jugs, A and B, and an infinite supply of water. There are three types of actions that you can use: (1) you can fill a jug, (2) you can empty a jug, and (3) you can pour from one jug to the other. Pouring from one jug to the other stops when the first jug is empty or the second jug is full, whichever comes first. For example, if A has 5 gallons and B has 6 gallons and a capacity of 8, then pouring from A to B leaves B full and 3 gallons in A.

    A problem is given by a triple (Ca,Cb,N), where Ca and Cb are the capacities of the jugs A and B, respectively, and N is the goal. A solution is a sequence of steps that leaves exactly N gallons in jug B. The possible steps are

    fill A 
    fill B 
    empty A 
    empty B 
    pour A B 
    pour B A 
    success

    where "pour A B" means "pour the contents of jug A into jug B", and "success" means that the goal has been accomplished.

    You may assume that the input you are given does have a solution.

输入

Input to your program consists of a series of input lines each defining one puzzle. Input for each puzzle is a single line of three positive integers: Ca, Cb, and N. Ca and Cb are the capacities of jugs A and B, and N is the goal. You can assume 0 < Ca <= Cb and N <= Cb <=1000 and that A and B are relatively prime to one another. 

输出

Output from your program will consist of a series of instructions from the list of the potential output lines which will result in either of the jugs containing exactly N gallons of water. The last line of output for each puzzle should be the line "success". Output lines start in column 1 and there should be no empty lines nor any trailing spaces.

样例输入

3 7 1
9 32 6

样例输出

fill B
pour B A
empty A
pour B A
success
fill B
pour B A
empty A
pour B A
empty A
pour B A
empty A
pour B A
fill B
pour B A
empty A
pour B A
empty A
pour B A
empty A
pour B A
empty A
pour B A
fill B
pour B A
empty A
pour B A
empty A
pour B A
success

提示


 倒水问题的经典形式是这样的:
  “假设有一个池塘,里面有无穷多的水。现有2个空水壶,容积分别为5升和6升。问题是如何只用这2个水壶从池塘里取得3升的水。”
  当然题外是有一些合理的限制的,比如从池塘里灌水的时候,不管壶里是不是已经有水了,壶一定要灌满,不能和另一个壶里的水位比照一下“毛估估”(我们可以假设壶是不透明的,而且形状也不同);同样的,如果要把水从壶里倒进池塘里,一定要都倒光;如果要把水从一个壶里倒进另一个壶里,也要都倒光,除非在倒的过程中另一个壶已经满了;倒水的时候水没有损失(蒸发溢出什么的)等等等等。
  事实上,要解决上面这题,你只要用两个壶中的其中一个从池塘里灌水,不断地倒到另一个壶里,当第二个壶满了的时候,把其中的水倒回池塘里,反复几次,就得到答案了。以5升壶(A)灌6升壶(B)为例:
     A  B
0  0
5  0  A→B
0  5
5  5  A→B
4  6
4  0  A→B
0  4
5  4  A→B
3  6

  现在我们问,如果是多于2只壶的情况怎么办(这样一来就不能用上面的循环倒水法了)?如何在倒水之前就知道靠这些壶是一定能(或一定不能)倒出若干升水来的?试举数例:
1) 两个壶:65升和78升,倒38升和39升。
2) 三个壶:6升,10升和45升,倒31升。

  我们可以看到,在1)中,65=5×13,78=6×13,而39=3×13。所以如果把13升水看作一个单位的话(原题中的“升”是没有什么重要意义的,你可以把它换成任何容积单位,毫升,加仑——或者“13升”),这题和最初的题目是一样的。而38升呢?显然是不可能的,它不是13的倍数,而65升和78升的壶怎么也只能倒出13升的倍数来。也可以这样理解:这相当于在原题中要求用5升和6升的壶倒出38/39升来。
  那么2)呢?你会发现,只用任何其中两个壶是倒不出31升水的,理由就是上面所说的,(6,10)=2,(6,45)=3,(10,45)=5,(这里(a,b)是a和b的最大公约数),而2,3,5均不整除31。可是用三个壶就可以倒出31升:用10升壶四次,6升壶一次灌45升壶,得到1升水,然后灌满10升壶三次得30升水,加起来为31升。
  一般地我们有“灌水定理”:
  “如果有n个壶容积分别为A1,A2,……,An(Ai均为大于0的整数)设w为另一大于0的整数。则用此n个壶可倒出w升水的充要条件为:
1) w小于等于A1+A2+......+An;
2) w可被(A1,A2,......,An)(这n个数的最大公约数)整除。”

  这两个条件都显然是必要条件,如果1)不被满足的话,你连放这么多水的地方都没有。2)的道理和上面两个壶的情况完全一样,因为在任何步骤中,任何壶中永远只有(A1,A2,......,An)的倍数的水。
  现在我们来看一下充分性。在中学里我们学过,如果两个整数a和b互素的话,那么存在两个整数u和v,使得ua+vb=1。证明的方法很简单:在对a和b做欧几里德辗转相除时,所有中间的结果,包括最后得到的结果显然都有ua+vb的形式(比如第一步,假设a小于b,记a除b的结果为s,余数为t,即b=sa+t,则t=(-s)a+b,即u=-s,v=1)。而两个数互素意味着欧几里德辗转相除法的最后一步的结果是1,所以1也可以记作ua+vb的形式。稍微推广一点,如果(a,b)=c,那么存在u和v使得ua+vb=c(两边都除以c就回到原来的命题)。
  再推广一点,如果A1,A2,……,An是n个整数,(A1,A2,......,An)=s,那么存在整数U1,U2,……,Un,使得
       U1A1 + U2A2 + ...... + UnAn = s.    (*)
  在代数学上称此结果为“整数环是主理想环”。这也不难证,只要看到
    (A1,A2,A3,A4,......,An) = ((((A1,A2),A3),A4),......,An).
  也就是说,可以反复应用上一段中的公式:比如三个数a,b,c,它们的最大公约数是d。假设(a,b)=e,那么(e,c)=((a,b),c)=d。现在有u1,u2使得u1a+u2b=e,又有v1,v2使得v1e+v2c=d,那么
      (v1u1)a+(v1u2)b+(v2)c=d.
  好,让我们回头看“灌水定理”。w是(A1,A2,......,An)的倍数,根据上节的公式(*),两边乘以这个倍数,我们就有整数V1,V2,……,Vn使得 V1A1 + V2A2 + ...... + VnAn = w.注意到Vi是有正有负的。
  这就说明,只要分别把A1,A2,……,An壶,灌上V1,V2,……,Vn次(如果Vi是负的话,“灌上Vi次”要理解成“倒空-Vi次”),就可以得到w升水了。具体操作上,先求出各Vi,然后先往Vi是正数的壶里灌水,灌1次就把Vi减1。再把这些水到进Vi是负数的壶里,等某个壶灌满了,就把它倒空,然后给这个负的Vi加1,壶之间倒来倒去不变更各Vi的值。要注意的是要从池塘里灌水,一定要用空壶灌,要倒进池塘里的水,一定要是整壶的。这样一直到所有Vi都是0为止。
  会不会发生卡住了,既不能灌水又不能倒掉的情况?不会的。如果有Vi仍旧是负数,而Ai壶却没满:那么如果有其它Vi是正的壶里有水的话,就都倒给它;如果有其它Vi是正的壶里没水,那么就拿那个壶打水来灌(别忘了给打水的壶的Vi减1);如果根本没有任何Vi是正的壶了——这是不可能的,这意味着w是负的。有Vi仍旧是正数,而Ai壶却没满的情况和这类似,你会发现你要用到定理中的条件1)。
  这样“灌水定理”彻底得证。当然,实际解题当中如果壶的数目和容积都比较大的话,手工来找(*)中的各Ui比较困难,不过可以写个程序,连倒水的步骤都算出来。最后要指出的一点是,(*)中的Ui不是唯一的,所以倒水的方式也不是唯一的。

一般地我们有“灌水定理”:
  “如果有n个壶容积分别为A1,A2,……,An(Ai均为大于0的整数)设w为另一大于0的整数。则用此n个壶可倒出w升水的充要条件为:
1) w小于等于A1+A2+......+An;
2) w可被(A1,A2,......,An)(这n个数的最大公约数)整除。”

#include<iostream>
#include<algorithm>
using namespace std;
int main() {
	//打断点还是很有用的
	//freopen("input3.txt","r",stdin);
	int ca, cb, n;
	int A = 0, B = 0;
	while (cin >> ca >> cb >> n) {
		A = B = 0;
		while (true) {
			if (B == 0) {
				cout << "fill B\n";
				B = cb;
			}
			int add = min(B,ca-A);
			B -= add;
			A += add;
			cout << "pour B A\n";			

			if (B == n) {
				cout << "success\n";
				break;
			}

			if (A == ca) {
				cout << "empty A\n";
				A = 0;
			}
		}
	}
	return 0;
}

问题 B: DFS or BFS?

时间限制: 1 Sec  内存限制: 128 MB
提交: 429  解决: 93
[提交][状态][讨论版][命题人:外部导入]

题目描述

说好了,题目不黑人。

给你一个8*8的矩阵,你的初始位置是左下角方格(用'U’表示),你的目标位置是右上角的方格(用'A'表示),其余的62个方格,如果是'.',表示这个方格为空,如果是'S',表示这个方格有一块大石头。好了现在你开始从左下角出发,每次可以往上,下,左,右,左上,右上,左下,右下移动一个方格,或者你可以原地不动,一共九个动作方式,在你做完一个动作后,所有的大石头会往下掉一个方格(如果一个大石头的位置是(x,y),那下一秒是(x+1,y),不过如果它已经在最下面的一排了,那它就会掉出矩阵,不再出现),请注意,任一时刻,你不能和某一个大石头处在同一个方格,否则石头会把你XX掉。

现在的问题就是:你能从左下角安全抵达右上角么? 如果能,输出“Yes”,反之,“No”。

输入

T->测试数据组数(T)。

对于每组数据,输入一个8*8的矩阵,其后有一空行。描述如上。

输出

对于第i组数据,请输出

Case #i: s(s是一个字符串,如果可以到达,则s为“Yes”,反之“No”)

样例输入

2
.......A
........
........
........
........
........
........
U.......

.......A
........
........
........
........
.S......
S.......
US......

样例输出

Case #1: Yes
Case #2: No

本题目不要想着去改数组 超级麻烦,直接利用根据原始状态step推算出step步后的情况即可,(且step>8 时队列还未空,则一定能到达,因为当时有路可走,而8步过后即使有石头也落完了,没有阻碍当然能到达了)

#include<iostream>
#include <queue>
using namespace std;

const int N = 8;
char a[N + 1][N + 1];

struct Node
{
	int x, y;
	int step;
	Node() {}
	Node(int x, int y, int step) {
		this->x = x;
		this->y = y;
		this->step = step;
	}
}Start, top, Next;

bool judge(Node node) {
	if (node.x<0 || node.x >= N || node.y<0 || node.y >= N) return false;
	if (node.x - (node.step - 1) >= 0 && a[node.x - (node.step - 1)][node.y] == 'S') return false;//当时此点就为S
	if (node.x - node.step >= 0 && a[node.x - node.step][node.y] == 'S') return false;//走完后上面是S 会被砸到
	return true;
}


bool BFS() {
	queue<Node> q;
	q.push(Start);//step初始为0
	while (!q.empty()) {
		top = q.front();
		q.pop();

		for (int i = -1;i <= 1;i++) {
			for (int j = -1;j <= 1;j++) {
				Next = Node(top.x + i, top.y + j, top.step + 1);
				if (judge(Next)) {
					if (a[Next.x][Next.y] == 'A' || Next.step >= 8) {//走了8步石头会落完 此时还没死肯定能到达
						return true;
					}
					q.push(Next);
				}
			}
		}
	}
	return false;
}

int main() {
	// freopen("input2.txt","r",stdin);
	int T;
	int Count = 0;
	Start = Node(7, 0, 0);//层数初始化为0
	cin >> T;
	while (T--) {
		for (int i = 0;i<N;i++) {
			for (int j = 0;j<N;j++) {
				cin >> a[i][j];
			}
		}

		Start.step = 0;
		if (BFS()) {
			cout << "Case #" << ++Count << ": Yes" << endl;
		}
		else {
			cout << "Case #" << ++Count << ": No" << endl;
		}
	}
	return 0;
}

问题 C: 【宽搜入门】8数码难题

时间限制: 20 Sec  内存限制: 128 MB
提交: 151  解决: 48
[提交][状态][讨论版][命题人:外部导入]

题目描述

初始状态的步数就算1,哈哈

输入:第一个3*3的矩阵是原始状态,第二个3*3的矩阵是目标状态。 
输出:移动所用最少的步数 

Input

2 8 3 
1 6 4 
7 0 5 
1 2 3 
8 0 4 
7 6 5 

Output

6

注:

1.题意描述不清,补充题意

在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局。求最小移动步数

2.只有0可以移动,那么每次相当于0走了一步,直到达到目标状态时结束(出口)也即是=》0上下左右BFS

面向对象的写法,代码比较长

#include<iostream>
#include <queue>
#include <algorithm>
using namespace std;

const int N=3;
int a[N][N],final[N][N];
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};

struct Node
{
	int x,y;
	int step;
	int pre[2];//上一个点的坐标
	int a[N][N];//当前的九宫格状态

	Node(){}
	Node(int x,int y,int step){
		this->x=x;
		this->y=y;
		this->step=step;
	}

	void setA(int m[N][N]){
		for(int i=0;i<N;i++){
			for(int j=0;j<N;j++){
				a[i][j]=m[i][j];
			}
		}
	}

	bool isGoal(){
		for(int i=0;i<N;i++){
			for(int j=0;j<N;j++){
				if(a[i][j]!=final[i][j]) return false;
			}
		}
		return true;
	}

}Start,Next,Top;

bool judge(Node node,Node top){
	if(node.x<0||node.x>=3||node.y<0||node.y>=3) return false;
	if(node.x==top.pre[0]&&node.y==top.pre[1]) return false;//回到上次的点了 明显白走
	return true;
}

int BFS(int x,int y){
	queue<Node> q;
	Start=Node(x,y,1);
	Start.pre[0]=x,Start.pre[1]=y;
	Start.setA(a);
	if(Start.isGoal()){
		return 1;//开始就是好的
	}
	q.push(Start);//起始结点不能忘记push

	while(!q.empty()){
		Top=q.front();
		q.pop();
		for(int i=0;i<4;i++){
			Next=Node(Top.x+dx[i],Top.y+dy[i],Top.step+1);
			Next.pre[0]=Top.x,Next.pre[1]=Top.y;
			if(judge(Next,Top)){
				Next.setA(Top.a);
				swap(Next.a[Top.x][Top.y],Next.a[Next.x][Next.y]);//0走一步
				if(Next.isGoal()) return Next.step;//达到目标
				q.push(Next);
			}
		}
	}
	return -1;//无解
}

int main(){
	// freopen("in3.txt","r",stdin);
	int firstValue;
	int x,y;//0的位置
	while(cin>>firstValue){
		for(int i=0;i<N;i++){
			for(int j=0;j<N;j++){
				if(i==0&&j==0){
					a[i][j]=firstValue;
				}else{
					cin>>a[i][j];
				}
				if(a[i][j]==0){
					x=i;y=j;
				}
			}
		}

		for(int i=0;i<N;i++){
			for(int j=0;j<N;j++){
				cin>>final[i][j];
			}
		}
		cout<<BFS(x,y)<<endl;
	}
	return 0;
}

1.转换为0的移动问题(每次移动其实相当于0走了一步(和走到的位置交换数字))
2.最小步数,想到BFS往往用来求最短路径,则转换为求0走的最短路径问题
3.不能写bool数组标记已经访问过,因为拼图问题大多数情况下都要走回头路,即0(拼图中的空白)又移到到之前到过的位置
4.只是防止了相邻格式来回走动,也不会陷入死循环,因为BFS本质上是一次尝试多条路径(一次循环尝试走到下一层的每条)
可能路径。即使有部分死循环,也会在陷入死循环那条路径的同时尝试其他有效路径,一旦尝试到有效路径,就立刻结束
(并不是q.empty()成立才退出BFS)

 

问题 D: 【宽搜入门】魔板

时间限制: 1 Sec  内存限制: 128 MB
提交: 148  解决: 37
[提交][状态][讨论版][命题人:外部导入]

题目描述

在成功地发明了魔方之后,鲁比克先生发明了它的二维版本,称作魔板。这是一张有8个大小相同的格子的魔板: 
1 2 3 4 
8 7 6 5 
我们知道魔板的每一个方格都有一种颜色。这8种颜色用前8个正整数来表示。可以用颜色的序列来表示一种魔板状态,规定从魔板的左上角开始,沿顺时针方向依次取出整数,构成一个颜色序列。对于上图的魔板状态,我们用序列(1,2,3,4,5,6,7,8)来表示。这是基本状态。 
这里提供三种基本操作,分别用大写字母“A”,“B”,“C”来表示(可以通过这些操作改变魔板的状态): 
“A”:交换上下两行; 
“B”:将最右边的一列插入最左边; 
“C”:魔板中央四格作顺时针旋转。 
下面是对基本状态进行操作的示范: 
A: 
8 7 6 5 
1 2 3 4 
B: 
4 1 2 3 
5 8 7 6 
C: 
1 7 2 4 
8 6 3 5 
对于每种可能的状态,这三种基本操作都可以使用。 
你要编程计算用最少的基本操作完成基本状态到目标状态的转换,输出基本操作序列。 
【输入格式】 
输入有多组测试数据 
只有一行,包括8个整数,用空格分开(这些整数在范围 1——8 之间),表示目标状态。 
【输出格式】 
Line 1: 包括一个整数,表示最短操作序列的长度。 
Line 2: 在字典序中最早出现的操作序列,用字符串表示,除最后一行外,每行输出60个字符。 

Sample Input

2 6 8 4 5 7 3 1

Sample Output

7

BCABCCB 

注意:

1.本题关键在于hash剪枝,将所有可能的排列映射成hash,然后去重,此步不做超时超内存,卡了我好久好久!就知道前后不可重复,就是不知道整体去重,大大减少了重复!(8个数转成8进制hash即可 不用9进制 虽然出现了数字8)

2.然后就是题目求的是初始态到目标的步骤,弄反了

3.A,B,C的操作其实直接利用一个中间变量char t来一个一个转还方便些,逻辑还清晰些,一下就写好了,不用分析都!

4.步数长度不定 用vector存肯定是最好啦,都不用step了

5.最后。以后写BFS,结构体定义得简单点,不要当做c++的类来用,写一大推成员函数,那样代码反而更长了。

ac源码:

#include<iostream>
#include <queue>
#include <vector>
#include <cstring>
using namespace std;

int Goal[8];
const int N=18831569+10;
int Hash[N];

//转成8进制数吧
int toHash(int a[]){
	return a[0]*(8*8*8*8*8*8*8)+a[1]*(8*8*8*8*8*8)+a[2]*(8*8*8*8*8)
			+a[3]*(8*8*8*8)+a[4]*(8*8*8)+a[5]*(8*8)
			+a[6]*(8)+a[7];
}

void op(char c, int* a) {
	char t;
	if (c == 'A') {
		for(int i=0;i<4;i++){
			t=a[i];a[i]=a[7-i];a[7-i]=t;
		}
	}
	else if (c == 'B') {
		//第一行
		t=a[0];a[0]=a[3];a[3]=a[2];a[2]=a[1];a[1]=t;

		//第二行  直接自己转其实逻辑还简单清晰些 有时不要过分依赖API
		t=a[7];a[7]=a[4];a[4]=a[5];a[5]=a[6];a[6]=t;
	}
	else {
		t=a[1];a[1]=a[6];a[6]=a[5];a[5]=a[2];a[2]=t;
	}
}

struct Magic
{
	int A[8];
	vector<char> opchar;
}Start, Top, Next;

char opchar[3] = { 'A','B','C' };

bool isGoal(int A[9]) {
	for (int i = 0;i<8;i++) {
		if (A[i] != Goal[i]) return false;
	}
	return true;
}

void BFS(int a[]) {
	queue<Magic> q;
	for(int i=0;i<8;i++) Start.A[i]=a[i];
	if(isGoal(a)){
		cout<<"0\n";
		return;
	}
	q.push(Start);
	while (!q.empty()) {
		Top = q.front();
		q.pop();
		for (int i = 0;i<3;i++) {
			for(int i=0;i<8;i++) Next.A[i]=Top.A[i];
			Next.opchar=Top.opchar;
			Next.opchar.push_back(opchar[i]);
			op(opchar[i],Next.A);
			int hh=toHash(Next.A);
			if(Hash[hh]==0){//这种状态之前出现过了,不用再入队了 重复 
								//减枝一大片 前面的三种其实都不用了
				Hash[hh]=1;
			}else{
				continue;
			}
			if (isGoal(Next.A)) {//Next不是Top 千万别被自己绕晕了 关键时候立即打断点
				cout << Next.opchar.size() << endl;
				for (int i = 0;i < Next.opchar.size();i++) {
					cout << Next.opchar[i];
				}
				cout << endl;
				return;
			}
			else {
				q.push(Next);
			}
		}
	}
	//cout << "无解\n";
	//return;
}

int main() {
	// freopen("input4_1.txt", "r", stdin);
	int a[] = { 1,2,3,4,5,6,7,8 };//初始状态
	while (cin >> Goal[0]) {
		for (int i = 1;i<8;i++) {
			cin >> Goal[i];
		}
		memset(Hash,0,sizeof(Hash));
		BFS(a);
	}
	return 0;
}

问题 E: 【宽搜入门】巧妙取量

时间限制: 2 Sec  内存限制: 128 MB
提交: 176  解决: 55
[提交][状态][讨论版][命题人:外部导入]

题目描述

【题目描述】 
  有三个容器,容量分别为 a,b,c(a> b > c ),一开始a装满油,现在问是否只靠abc三个容器量出k升油。如果能就输出“yes”,并且说明最少倒几次,否则输出“no”。例如:10升油在10升的容器中,另有两个7升和3升的空容器,要求用这三个容器倒油,使得最后在abc三个容器中有一个刚好存有5升油,问最少的倒油次数是多少?(每次倒油,A容器倒到B容器,或者A内的油倒完,或者B容器倒满。 
 10 7 3 
(10 0 0) 
(3 7 0):第一次 
(3 4 3):第二次 
(6 4 0):第三次 
(6 1 3):第四次 
(9 1 0):第五次 
(9 0 1):第六次 
(2 7 1):第七次 
(2 5 3):第八次,出现5了。

Input

【输入格式】 
  有多组测试数据。 
  输入a,b,c, k四个正整数( 100 ≥ a > b > c≥1 , 1≤k< 100 )

Output

【输出格式】 
  如果能得到k就输出两行。 
  第一行“yes”,第二行为最少的次数 
  否则输出“no” 

Sample Input

10 7 3 5

Sample Output

yes
8

hash去重

#include<iostream>
#include <queue>
#include <cstring>
using namespace std;

int A[3],k;
const int N=1000200;
int Hash[N];
//1~100 3位 化为3位100进制
int toHash(int a[3]){
	return a[0]*100*100+a[1]*100+a[2];
}

struct Node
{
	int a[3],step;
	int lasti,lastj;
	Node(){a[0]=a[1]=a[2]=step=0;}
}Start,Top,Next;

void BFS(){
	queue<Node> q;
	Start.a[0]=A[0];
	if(A[0]==k){
		cout<<"yes\n0\n";
		return;
	}
	q.push(Start);
	while(!q.empty()){
		Top=q.front();
		q.pop();

		Next.step=Top.step+1;
		for(int i=0;i<3;i++){
			for(int j=0;j<3;j++){
				if(i==j) continue;
				//a[i]->a[j]
				if(Top.a[j]<A[j]&&Top.a[i]!=0){
					for(int i=0;i<3;i++) Next.a[i]=Top.a[i];
					int n=min(Top.a[i],A[j]-Top.a[j]);
					Next.a[i]-=n;
					Next.a[j]+=n;

					if(Next.a[0]==k||Next.a[1]==k||Next.a[2]==k){
						cout<<"yes\n";
						cout<<Next.step<<endl;
						return;
					}

					int hh=toHash(Next.a);
					if(Hash[hh]==0){
						Hash[hh]=1;
					}else{
						continue;//出口是啥呢?  重复的我不倒了 迟早会q.empty()的 妙
					}
					q.push(Next);
				}
			}
		}		

	}
	cout<<"no\n";
	return;
}


int main(){
	// freopen("input5.txt","r",stdin);
	while(cin>>A[0]>>A[1]>>A[2]>>k){
		memset(Hash,0,sizeof(Hash));
		BFS();
	}
	return 0;
}

小结:

二维数组的题目,N小于20的,适用DFS。而一般 N<= 200,N<=1000这种,一定不可能用DFS去做。而且并不只是整个题目不能用DFS,其中的每一步也不能使用DFS。

一般来说用DFS解决的问题都可以用BFS来解决。

DFS(深搜的同时考虑回溯)

bfs=队列,入队列,出队列;dfs=栈,压栈,出栈

bfs是按一层一层来访问的,所以适合有目标求最短路的步数,你想想层层搜索每次层就代表了一步。bfs优先访问的是兄弟节点,只有这一层全部访问完才能访问下一层,也就是说bfs第几层就代表当前可以走到的位置(结点).而dfs是按递归来实现的,它优先搜索深度,再回溯,优先访问的是没有访问过的子节点

DFS多用于连通性问题因为其运行思想与人脑的思维很相似,故解决连通性问题更自然BFS多用于解决最短路问题,其运行过程中需要储存每一层的信息,所以其运行时需要储存的信息量较大,如果人脑也可储存大量信息的话,理论上人脑也可运行BFS。
总的来说多数情况下运行BFS所需的内存会大于DFS需要的内存(DFS一次访问一条路,BFS一次访问多条路),DFS容易爆栈(栈不易"控制"),BFS通过控制队列可以很好解决"爆队列"风险。
它们两者间各自的优势需要通过实际的问题来具体分析,根据它们各自的特点来应用于不同的问题中才能获得最优的性能。

猜你喜欢

转载自blog.csdn.net/hza419763578/article/details/88606154
8.2