廣度優先搜尋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()