1. 程式人生 > >廣度優先搜尋BFS(洛谷)

廣度優先搜尋BFS(洛谷)

ACM題集:https://blog.csdn.net/weixin_39778570/article/details/83187443
深度優先搜尋DFS(洛谷)
P1162 填塗顏色
題目:https://www.luogu.org/problemnew/show/P1162
題意:把被1包圍的0找出來,標記成2
解法:從周圍入手,把沒被包圍的找出來就行來,標記為另外的數字區分開來

//P1162 填塗顏色 四個邊把0搜完就行了。。。 
#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
#define mk make_pair using namespace std; int n, a[35][35]; int dx[] = {-1,0,0,1}; int dy[] = {0,-1,1,0}; void bfs(int x, int y){ queue<pair<int,int> > q; q.push(mk(x,y)); while(!q.empty()){ pair<int,int> p = q.front(); a[p.first][p.second] = 3; q.pop(); fo(i,0,3){ int nowx =
p.first+dx[i]; int nowy = p.second+dy[i]; if(nowx>=1&&nowx<=n&&nowy>=1&&nowy<=n&&a[nowx][nowy]==0){ q.push(mk(nowx,nowy)); } } } } void solve(){ fo(i,1,n){ if(a[1][i]==0)bfs(1,i); if(a[i][1]==0)bfs(i,1); if(a[i][n]==0)bfs(i,n); if(a[
n][i]==0)bfs(n,i); } fo(i,1,n)fo(j,1,n){ if(a[i][j]==3)printf("%d%c",0,j==n?'\n':' '); else if(a[i][j]==1)printf("%d%c",1,j==n?'\n':' '); else if(a[i][j]==0)printf("%d%c",2,j==n?'\n':' '); } } int main(){ scanf("%d",&n); fo(i,1,n)fo(j,1,n){ scanf("%d",&a[i][j]); } solve(); return 0; }

P1032 字串變換
題目:https://www.luogu.org/problemnew/show/P1032
題意:已知有兩個字串A,B及一組字串變換的規則(至多6個規則),問從A串能否變到B串,如果變換次數大於10也算不能變換
廣搜列舉問題狀態空間,逐層遞推
但是這樣直接搜尋會爆記憶體
解決MLE問題,當某狀態空間已經向下一層遍歷過了,就不再將該狀態加入下一層狀態空間中,因為即便能找到答案,步數也比之前加入的多,
而且最重要的問題是重複枚舉了
廣搜結束,超過10步 或者找到問題的解
當遍歷完所有狀態任然為找到目標狀態,則無解
注意find函式的寫法(這個函式寫得不怎麼樣)

#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
#define mk make_pair 
using namespace std;
struct node{
	string s;
	int step;
	node(){}
	node(string s, int t):s(s),step(t){}
};
// pos 是匹配上的下標,如果能夠匹配上整串,返回匹配上的字串,否則返回"" 
// s1是原串,s2是匹配串,s3是改變的串,從s1的pos位置開始匹配 
string find(const string &s1,const string &s2,const string &s3, int pos){
	int fir = pos;
	for(int i=0; i<s2.length(); ++i){
		if(s1[pos]==s2[i]){
			pos++;
		}else{
			return "";
		}
	}
	string s;
	s = s1.substr(0,fir); 
	s += s3;
	s += s1.substr(pos);
	// 差不多的寫法 
//	for(int i=0; i<fir; ++i){
//        s+=s1[i];
//    }
//    for(int i=0; i<s3.length(); ++i){
//        s+=s3[i];
//    }
//    for(int i=pos;i<s1.length();++i){
//        s+=s1[i];
//    }
	return s;
}
string st,ed;
vector<pair<string,string> > sss;
map<string,int> mp;
void bfs(){
	queue<node> q;
	string s = st;
	q.push(node(s,0));
	while(!q.empty()){
		node e = q.front();
		q.pop();
		if(e.s==ed){
			if(e.step<=10)printf("%d\n",e.step);
			else puts("NO ANSWER!");
			return;
		}
		if(e.step>10){
			puts("NO ANSWER!");
			return;
		}
		if(mp[e.s])continue; // 該狀態空間列舉過了 
		mp[e.s]=1;
		int len2 = e.s.length();
		for(auto t : sss){
			for(int i=0; i<len2; ++i){
				if(e.s[i]==t.first[0]){
					string temp = find(e.s,t.first,t.second,i);
					if(temp!=""){
						q.push(node(temp, e.step+1));
					}
				}
			}
		}
	}
	puts("NO ANSWER!");
}
int main(){
	cin>>st>>ed;
	string ss1,ss2;
	while(cin>>ss1>>ss2){
		sss.push_back(mk(ss1,ss2));
	}
	bfs();
	return 0;
}

P1141 01迷宮
題目:https://www.luogu.org/problemnew/show/P1141
題意:求每個點能到達的點的個數
解法:這題直接列舉會T
仔細把圖畫出來,其實每一個相同的聯通塊,01010101…連在一起的是同一個聯通塊,他們所能訪問到的個數就是本聯通塊的個數,聯通塊中的個體答案一樣的
於是我們維護標記x,y屬於哪一個聯通塊,通過n*x+y來一一對映該座標所在的聯通塊是否被計算過了,避免重複計算答案
本題可以採用廣搜和深搜兩種方案

#include<bits/stdc++.h>
#define ll long long
#define mk make_pair
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;

char s[1005][1005];
bool vis[1005][1005];
int n,m;
int dx[] = {-1,1,0,0};
int dy[] = {0,0,-1,1}; 
int father[1005][1005],Ans[1005555]; // 這個要開大 1000*1000+1000+1 
void bfs(int fx,int fy){
	queue<pair<int, int> >q;
//	memset(vis, 0, sizeof(vis));
	q.push(mk(fx,fy));	
	int ans = 0;
	while(!q.empty()){
		pair<int,int> p = q.front();
		q.pop();
		int x = p.first, y = p.second; 
		if(vis[x][y]) continue;
		vis[x][y] = 1;
		ans++;
		father[x][y] = fx*n+fy;
		fo(i,0,3){
			int nowx = x+dx[i];
			int nowy = y+dy[i];	
			if(nowx>=1&&nowx<=n&&nowy>=1&&nowy<=n&& !vis[nowx][nowy] && s[x][y]!=s[nowx][nowy]){
				q.push(mk(nowx,nowy));
			}
		}
	}
	Ans[fx*n+fy] = ans;
}
int main(){
	scanf("%d%d",&n,&m);
	fo(i,1,n){
		scanf("%s",s[i]+1);
	}
	int x,y;
	fo(i,1,m){
		scanf("%d%d",&x,&y);
		if(!father[x][y])bfs(x,y);
		printf("%d\n",Ans[father[x][y]]);
	}
	return 0;
}

簡潔優美的dfs版本,聯通塊的劃分真的很方便

#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; i++)
using namespace std;
const int maxn = 1005;
int dx[] = {-1,1,0,0};
int dy[] = {0,0,-1,1};
char s[maxn][maxn];
int n,m;
int vis[maxn][maxn],size[1000005],cnt; // 最多有n*n個聯通塊 
void dfs(int x,int y){
	vis[x][y]=cnt; // (x,y) 所屬聯通塊 
	size[cnt]++;   // 計算該聯通塊的大小 
	fo(i,0,3){
		int nowx = x+dx[i];
		int nowy = y+dy[i];
		if(nowx>=1&&nowx<=n&&nowy>=1&&nowy<=n && s[x][y]!=s[nowx][nowy] && !vis[nowx][nowy]){
			dfs(nowx,nowy);
		}
	}
}
int main(){
	scanf("%d%d",&n,&m);
	fo(i,1,n)scanf("%s",s[i]+1);
	int x,y;
	fo(i,1,m){
		scanf("%d%d",&x,&y);
		if(!vis[x][y]){
			cnt++;  // 聯通塊增加 
			dfs(x,y);
		}
		printf("%d\n", size[vis[x][y]]);
	}
	return 0;
} 

P1126 機器人搬重物
題目:https://www.luogu.org/problemnew/show/P1126
題意:給出幾種指令,問機器人從起點到終點的最短走路時間
解法:注意機器人只能在格線上走,於是我們不妨設格子也為格線,右格子左下角的點代替(相應地調整一下機器人的起點和終點,列加1,使得跟網格對上),另外注意的是機器人是有體積的,然後按各種指令對機器人的狀態進位制廣搜一遍就好,機器人的狀態指的的是(座標x,座標y,頭方向),使用vis陣列標記搜過了就不要重複搜了

#include<bits/stdc++.h>
#define ll long long
#define mk makr_pair
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
struct node{
	int x, y, fx, step;
	node(){}
	node(int x,int y,int fx,int step):x(x),y(y),fx(fx),step(step){}
};
int dx[] ={-1,1,0,0};// 上下左右NSWE ,1步 
int dy[] ={0,0,-1,1};
int n,m;
int a[55][55]; // 座標為每個方格的左下角 
bool vis[5][55][55];
int stx,sty,edx,edy;
bool ok(int x,int y){
	if(x>=1&&x<n&&y>1&&y<=m){ // n,m不要搞錯啦,搞成n+1也過了,資料實在太弱了 
		if(!a[x][y] && !a[x+1][y] && !a[x][y-1] &&!a[x+1][y-1]){
	//		cout<<x<<" "<<y<<" yes"<<endl;
			return true;
		}
	}
//	cout<<x<<" "<<y<<" no"<<endl;
	return false;
}
void bfs(int x, int y, char fx){
	queue<node> q;
	int f;if(fx=='N')f=0;else if(fx=='S')f=1;else if(fx=='W')f=2;else f=3; 
	q.push(node(x,y,f,0));
	while(!q.empty()){
		node p = q.front()