BFS
-
BFS概要
BFS是一项基本的暴力搜索技术,常用于解决图和树的遍历问题。BFS类似逐层遍历,其实现依托队列。可以应用在走迷宫、寻找最短路径等问题上。 -
注意点
1.标记。搜索的时候要及时标记,避免重复访问。
2.剪枝。搜索的时候判断搜索方向是否合理,不能达到目的的搜索方向及时终止。 -
扩展
康托展开
A*算法—搜索+贪心(策略)//bool vis[LEN+5]; //long int fac[10]={1,1,2,6,24,120,720,5040,40320,362880}; //八数码问题中的康托展开判重 bool Cantor(int str[], int n) //未访问返回true,并标记 { long result=0; for(int i=0; i<n; i++) { int counted=0; for(int j=i+1; j<n; j++) { if(str[i]>str[j]) counted++; } result += counted*fac[n-i-1]; } if(!vis[result]) { vis[result] = true; v++; return true; } return false; }
-
练习
Red and Black
Catch That Cow
Find The Multiple
Prime Path
Pots
Lake Counting大意:一个人站在黑色的瓷砖上。从瓷砖中,他可以移动到四个相邻瓷砖中的一个。但他不能在红瓦上移动,他只能在黑色瓷砖上移动。编写程序,通过重复上述动作来计算他可以达到的黑色瓷砖的数量。BFS和DFS都可行,主要是对访问过的进行标记。
#include<bits/stdc++.h> using namespace std; typedef struct { int x, y; }node; int m, n; int d[4][2]={{0,1},{0,-1},{-1,0},{1,0}}; queue<node> q; char r[25][25]; int main() { while(1) { cin>> n>> m; if(m==0 && n==0) break; node p; int sum=0; for(int i=0; i<m; i++) { for(int j=0; j<n; j++) { cin>> r[i][j]; if(r[i][j] == '@') { p.x = j; p.y = i; } } } q.push(p); r[p.y][p.x] = '#'; while(!q.empty()) { p = q.front(); q.pop(); sum++; for(int i=0; i<4; i++) { node temp=p; temp.x += d[i][0]; temp.y += d[i][1]; if(temp.x<n && temp.x>=0 && temp.y>=0 && temp.y<m && r[temp.y][temp.x] != '#') { q.push(temp); r[temp.y][temp.x] = '#'; } } } cout<< sum<< endl; } return 0; }
大意:农夫知道一头牛的位置,想要抓住它。农夫和牛都于数轴上 ,农夫起始位于点 N(0<=N<=100000) ,牛位于点 K(0<=K<=100000) 。农夫有两种移动方式: 1、从 X移动到 X-1或X+1 ,每次移动花费一分钟 2、从 X移动到 2*X ,每次移动花费一分钟 假设牛没有意识到农夫的行动,站在原地不。最少要花多少时间才能抓住牛?
搜索时对搜索过的位置进行标记,对于出界的位置不加入队列,数组在预定义的时候比需要的多一些,不然会RE#include<iostream> #include<queue> #include<string.h> using namespace std; typedef struct { int N, T; }node; int N, K; bool vis[1000000]; void BFS(int N, int K) { queue<node> q; node n={N, 0}; if(n.N == K) { cout<< "0"; return ; } q.push(n); vis[n.N] = true; while(!q.empty()) { node m; n=q.front(); q.pop(); m.N = n.N+1; if(vis[m.N] == false && m.N<=100000 && m.N>=0) { m.T = n.T+1; if(m.N == K) { cout<< m.T; return ; } vis[m.N] = true; q.push(m); } m.N = n.N-1; if(vis[m.N] == false && m.N<=100000 && m.N>=0) { m.T = n.T+1; if(m.N == K) { cout<< m.T; return ; } vis[m.N] = true; q.push(m); } m.N = n.N*2; if(vis[m.N] == false && m.N<=100000 && m.N>=0) { m.T = n.T+1; if(m.N == K) { cout<< m.T; return ; } vis[m.N] = true; q.push(m); } } } int main() { cin>> N>> K; memset(vis, false, sizeof(vis)); BFS(N, K); return 0; }
8发+之后终于抓到了这头牛
(最难抓的一头牛可能被我遇到了),数组莫名的开小了,所以,,,以后还是尽可能多开一点。大意就是找一个只由1、0组成的能被给定的数字整除的数。
#include <iostream> #include <cstdio> #include <cstring> using namespace std; int n; bool solved; void DFS(unsigned long long m,int k){ if(solved) return ; if(m%n == 0){ printf("%llu\n", m); solved = true; return ; } if(k == 19) return; DFS(m*10, k+1); DFS(m*10+1, k+1); } int main(){ while(cin>> n) { if(n == 0) break; solved = false; DFS(1,0); } return 0; }
大意: 将一个素数变成另一个素数,一次只能变一位数,并且转变的过程中也要是素数。输出最少的次数。先针对给出的数据范围对素数进行打表,避免超时。
#include<iostream> #include<cmath> #include<queue> #include<string.h> using namespace std; typedef struct { int a, p; }node; bool check[100000]; bool vis[100000]; int n; bool Prime(int m) { for(int i=2; i<=sqrt(m); i++) { if(m%i==0) return false; } return true; } void BFS(int a, int b) { memset(vis, false, sizeof(vis)); node x, y; queue<node> q; x.a = a; x.p = 0; q.push(x); vis[x.a] = true; while(!q.empty()) { x = q.front(); q.pop(); int t[4]; t[0] = x.a/1000;//千 t[1] = (x.a/100)%10;//百 t[2] = (x.a/10)%10;//十 t[3] = x.a%10;//个 for(int i=0; i<4; i++) { int temp = t[i]; for(int j=0; j<10; j++) { if(j == t[i]) continue; t[i] = j; y.a = t[0]*1000+t[1]*100+t[2]*10+t[3]; if(y.a == b) { cout<< x.p+1<< endl; return ; } if(check[y.a] && !vis[y.a]) { vis[y.a] = true; y.p = x.p+1; q.push(y); } } t[i] = temp; } } } int main() { for(int i=1001; i<10000; i++) { if(Prime(i)) { check[i] = true; } } cin>> n; for(int i=0; i<n; i++) { int a, b; cin>> a>> b; if(a==b) { cout<< 0<< endl; continue ; } vis[a] = true; BFS(a, b); } return 0; }
大意:给你两个杯子,容量分别是A,B,问你经过多少次的操作,能够使其中的一个杯子中的水量是C;一共有3种操作,分别是装满,全倒掉,把i杯子中的水倒给j杯子;
这道题需要依次输出倒水的方法,也就是需要记录路径,采用的是记录前驱的方法来对可以完成目标的方法进行连接的。最后根据链接从后向前将元素入栈,之后再依次出栈。#include<iostream> #include<queue> #include<stack> #include<string.h> using namespace std; typedef struct { int a, b;//状态 int step;//步数 int oper;//操作 int current;//当前位置 int parent;//前驱 }node; node cond[100000]; int c;//配套下标 queue<node> q; stack<node> S; bool vis[105][105]; int A, B, C; int s=1; //步数 node m; //临时 void Init(node &n,int a,int b, int s, int o, int c, int p) { n.a = a; n.b = b; n.step = s; n.oper = o; n.current = c; n.parent = p; } bool Operation(node& cur) { for(int i=0; i<6; i++) { c++; if(i==0)//A满 { Init(cond[c], A, cur.b, cur.step+1, 0, c, cur.current); } else if(i==1)//B满 { Init(cond[c], cur.a, B, cur.step+1, 1, c, cur.current); } else if(i==2)//A空 { Init(cond[c], 0, cur.b, cur.step+1, 2, c, cur.current); } else if(i==3)//B空 { Init(cond[c], cur.a, 0, cur.step+1, 3, c, cur.current); } else if(i==4)//A->B { if((B-cur.b)>=cur.a)//A全部倒入B { Init(cond[c], 0, cur.b+cur.a, cur.step+1, 4, c, cur.current); } else { Init(cond[c], cur.a-(B-cur.b), B, cur.step+1, 4, c, cur.current); } } else /*if(i==5)//B->A*/ { if((A-cur.a)>=cur.b)//B全部倒入A { Init(cond[c], cur.b+cur.a, 0, cur.step+1, 5, c, cur.current); } else { Init(cond[c], A, cur.b-(A-cur.a), cur.step+1, 5, c, cur.current); } } if(cond[c].a==C || cond[c].b==C) { m = cond[c]; return true; } if(!vis[cond[c].a][cond[c].b]) { vis[cond[c].a][cond[c].b] = true; q.push(cond[c]); } } return false; } bool BFS(int A, int B, int C) { c = 0; Init(cond[c], 0, 0, 0, 0, 0, 0); q.push(cond[c]); vis[0][0] = true; while(!q.empty()) { node cur; cur = q.front(); q.pop(); if(Operation(cur)) return true; } return false; } void Output() { cout<< m.step<< endl; while(m.current>0) { S.push(m); m = cond[m.parent]; } while(!S.empty()) { switch((S.top()).oper) { case 0: cout<<"FILL(1)"<<endl; break; case 1: cout<<"FILL(2)"<<endl; break; case 2: cout<<"DROP(1)"<<endl; break; case 3: cout<<"DROP(2)"<<endl; break; case 4: cout<<"POUR(1,2)"<<endl; break; case 5: cout<<"POUR(2,1)"<<endl; break; } S.pop(); } } int main() { cin>> A>> B>>C; memset(cond, 0, sizeof(cond)); memset(vis, false, sizeof(vis)); if(BFS(A, B, C)) Output(); else cout<< "impossible"; return 0; }
大意:给你一块地,W表示有水的地方,‘.’表示地是干的,若一个W与周围的W紧挨着则他们是一整块池塘,问你一共有多少块池塘
从第一行第一列开始扫,遇到W就进入dfs,向八个方向搜索,可以连接在一起形成池塘的水坑进行标记。#include<iostream> #include<string.h> #include<cstdlib> #include<cstdio> using namespace std; #define MAXN 101 char pool[MAXN][MAXN]; int N, M; int ans; int d[8][2]={{0, 1},{0, -1},{-1,0},{1,0},{-1,1},{1,1},{-1,-1},{1,-1}}; void DFS(int x, int y) { if(pool[x][y] == '.') return ; pool[x][y] = '.'; for(int i=0; i<8; i++) { int nx = x+d[i][0]; int ny = y+d[i][1]; if(nx>=0 && nx<N && ny>=0 && ny<M) { DFS(nx, ny); } } } int main() { cin>> N>> M; for(int i=0; i<N; i++) { getchar(); for(int j=0; j<M; j++) { scanf("%c", &pool[i][j]); } } for(int i=0; i<N; i++) { for(int j=0; j<M; j++) { if(pool[i][j] == 'W') { ans++; DFS(i, j); } } } cout<< ans; return 0; }