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; }