深度優先dfs與廣度bfs優先搜索總結+例題


DFS(Deep First Search)深度優先搜索

深度優先遍歷(dfs)是對一個連通圖進行遍歷的算法。它的思想是從一個頂點開始,沿着一條路一直走到底,如果發現不能到達目標解,那就返回到上一個節點,然后從另一條路開始走到底,這種盡量往深處走的概念即是深度優先的概念。

簡而言之:不撞南牆不回頭

模板如下:

void dfs(int t)//t代表目前dfs的深度
{
    if(滿足輸出條件||走不下去了)
    {
        輸出解;
        return;
    }
    else
    {
        for(int i=1;i<=嘗試方法數;i++)
            if(滿足進一步搜索條件)
            {
                為進一步搜索所需要的狀態打上標記;
                dfs(t+1);
                恢復到打標記前的狀態;//也就是說的{回溯一步}
            }
    }
}

例題一:洛谷P1219八皇后

#include<bits/stdc++.h>
using namespace std;
int a[14],b[14],c[29],d[29];//分別存橫、列、左對角線、右對角線訪問標記
int n;
int cnt=0;
void print()
{
	cnt++;
	if(cnt<=3)
	{
		for(int i=1;i<=n;i++)
			cout<<a[i]<<" ";
		cout<<endl;
	}
}
void dfs(int k)
{
	int i=k;
	for(int j=1;j<=n;j++)
	{
		if(b[j]==0&&c[i+j]==0&&d[i-j+n]==0)//滿足未被訪問
		{
			a[i]=j;
			b[j]=1;c[i+j]=1;d[i-j+n]=1;//分別在豎排,左對角線,右對角線打上標記
			if(k<n)
				dfs(k+1);	//放置下一橫排的皇后
			else if(k==n)
				print(); 
			b[j]=0;c[i+j]=0;d[i-j+n]=0;//回溯,標記重新置為0
		}			
	}
}
int main()
{
	cin>>n;
	dfs(1);
	cout<<cnt;
	return 0;
}

例題二:牛客網小雨的矩陣

#include<bits/stdc++.h>
using namespace std;
long long a[52][52];
int n;
set<int> s;	//set集合中數據唯一且有序

void dfs(int x,int y,int sum){
    if(x==n && y==n){
        s.insert(sum);	//走到(n,n)則把sum插入集合s
        return;
    }
    if(x+1<=n){
        dfs(x+1,y,sum+a[x+1][y]);//向下走
    }
    if(y+1<=n){
        dfs(x,y+1,sum+a[x][y+1]);//向右走
    }
}
int main() {
    cin>>n;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            cin>>a[i][j];	//錄入矩陣
        }
    }
    dfs(1,1,a[1][1]);	//開始深搜
    cout<<s.size()<<endl;//輸出集合s的大小
    return 0;
}

BFS(Breath First Search)廣度優先搜索

廣度優先搜索較之深度優先搜索之不同在於,深度優先搜索旨在不管有多少條岔路,先一條路走到底,不成功就返回上一個路口然后就選擇下一條岔路,而廣度優先搜索旨在面臨一個路口時,把所有的岔路口都記下來,然后選擇其中一個進入,然后將它的分路情況記錄下來,然后再返回來進入另外一個岔路,並重復這樣的操作。

簡而言之:地毯式搜索或者說像水波紋一樣四散開來

模板如下:

//通常用隊列queue實現,或者有些時候用數組模擬隊列
void bfs()
{
    初始化隊列q
    q.push(起點);
	標記上起點;
    while(!q.empty())
    {
		取隊首元素u;
         q.pop();//隊首元素出隊
         for(int i=0;i<可以走的方向數;i++)
         {
             if(下一步滿足邊界內,未訪問等條件)
             {
                 q.push();//該點入隊
                 標記上該點;
                 ...
             }
         }
    }
}

例題一:洛谷P1443馬的遍歷

此題要求馬從某點到達某點的最少要走幾步,優先用bfs

#include<bits/stdc++.h>
using namespace std;
int h[8]={-2,-1,1,2,-2,-1,1,2},z[8]={1,2,2,1,-1,-2,-2,-1};//8個方向 
int vis[410][410];
int cnt[410][410];//記錄到達每個坐標點的步數 
queue<pair<int,int> >q;
int n,m;
void bfs()
{
    while(!q.empty())
    {
        int x=q.front().first;
	    int y=q.front().second;
	    q.pop();
    	for(int i=0;i<8;i++)
    	{
		    int xx=x+h[i];
	    	int yy=y+z[i];
		    if(xx>n||xx<1||yy>m||yy<1||vis[xx][yy]==1)continue;//到達邊界或已經訪問則跳過此次循環
		    q.push(make_pair(xx,yy));
		    vis[xx][yy]=1;
		    cnt[xx][yy]=cnt[x][y]+1;
    	}
    }
}
int main()
{
	memset(cnt,-1,sizeof(cnt));
	int x,y;
	cin>>n>>m>>x>>y;
	vis[x][y]=1;
	cnt[x][y]=0;
	q.push(make_pair(x,y));
	bfs();
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			printf("%-5d",cnt[i][j]);//控制格式
		}
		cout<<endl;
	}
	return 0;
}

例題二:洛谷P1162填塗顏色

此題的關鍵是通過廣搜把 1 外圍的 0 打上標記

#include<bits/stdc++.h>
using namespace std;
int h[4]={-1,0,1,0},z[4]={0,-1,0,1};
int n,a[35][35]; 
queue<pair<int,int> >q;
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			cin>>a[i][j];
	for(int i=0;i<=n+1;i++)//在四周加0,避免在角落的0搜不過去
	{
		a[0][i]=0;
		a[n+1][i]=0;
		a[i][0]=0;
		a[n+1][i]=0;
	}
	q.push(make_pair(0,0));
	while(!q.empty())
	{
		int x=q.front().first;
		int y=q.front().second;
		q.pop();
		for(int i=0;i<4;i++)
		{
			int x2=x+h[i];
			int y2=y+z[i];
			if(x2>=0&&y2>=0&&x2<=n+1&&y2<=n+1&&a[x2][y2]==0)
			{
				a[x2][y2]=-1;//1外圍的0標志為-1
				q.push(make_pair(x2,y2)); 	
			}
		}
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(a[i][j]==-1)cout<<"0 ";
			else if(a[i][j]==1)cout<<"1 ";
			else if(a[i][j]==0)cout<<"2 ";
		}
		cout<<endl;
	}
	return 0;	
} 

綜上,其實很多題dfs和bfs都可以解,但是在最短(優)路徑問題上最好用廣度優先bfs


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM