深度優先搜索
1.判斷點是否能到終點
int main()
{
將所有點標記爲新點;
起點 = x;
終點 = y;
cout << dfs(起點);
}
bool dfs(V)
{
//邊界條件
if(v是終點)
return true;
if(v是舊點)
return false;
將v標記爲舊點 表示要訪問
for(跟v相鄰的所有的點u)
{
//返回true表示u能到終點 所以v也可以到終點
if(dfs(u) == true)
return true;
}
//一直到循環了所有的相鄰的點 不能到達終點 所以v也不能到
return false;
}
2.記錄路徑
//要記錄路徑,可以開一個數組,路徑最長就是點的個數。
Node path[MAX_LEN];
int depth; //表示深度,也就是路徑長度,初始爲0
int main()
{
將所有點都標記爲新點
depth = 0;
if(dfs(起點) == true)
{
//從0到depth,path[i]就是路徑上第i步的點
for(int i = 0;i <= depth; i++)
{
cout<<path[i]<<endl;
}
}
}
bool dfs(v)
{
//邊界條件
if(v爲終點)
{
path[depth] = v;
return true;
}
if(v是舊點)
return false;
將v標記爲已經訪問過
path[depth] = v; //嘗試把v放入到path
depth++; //深度應該加1
for(與v相鄰的所有點u)
{
if(dfs(u) == true)
return true;
}
//如果u到不了終點 那麼說明路徑上v不合適
depth--; //直接將depth--即可,下一次更新path數組時直接覆蓋
return false;
}
3.遍歷所有的點(不連通也可以)
int main()
{
將所有點標記爲新點
while(圖中能找到v是新點)
{
dfs(v);
}
}
void dfs(v)
{
if(v是舊點)
return ;
將v標記爲舊點
for(v相鄰的所有的點u)
{
dfs(u);
}
}
例題練習
題目1:城堡問題 OpenJ_Bailian - 2815
描述:
1 2 3 4 5 6 7
#############################
1 # | # | # | | #
#####---#####---#---#####---#
2 # # | # # # # #
#---#####---#####---#####---#
3 # | | # # # # #
#---#########---#####---#---#
4 # # | | | | # #
#############################
(圖 1)
# = Wall
| = No wall
- = No wall
圖1是一個城堡的地形圖。請你編寫一個程序,計算城堡一共有多少房間,最大的房間有多大。城堡被分>割成mn(m≤50,n≤50)個方塊,每個方塊可以有0~4面牆。
輸入:
程序從標準輸入設備讀入數據。第一行是兩個整數,分別是南北向、東西向的方塊數。在接下來的輸入行裏,每個方塊用一個數字(0≤p≤50)描述。用一個數字表示方塊周圍的牆,1表示西牆,2表示北牆,4表示東牆,8表示南牆。每個方塊用代表其周圍牆的數字之和表示。城堡的內牆被計算兩次,方塊(1,1)的南牆同時也是方塊(2,1)的北牆。輸入的數據保證城堡至少有兩個房間。輸出:
城堡的房間數、城堡中最大房間所包括的方塊數。結果顯示在標準輸出設備上。樣例輸入:
4
7
11 6 11 6 3 10 6
7 9 6 13 5 15 5
1 10 12 7 13 7 5
13 11 10 8 10 12 13
樣例輸出:
5
9
思路分析:
首先由題目可以知道,抽象爲模型,一個點可以到另一個點那麼這兩個點之間就有一條邊,那麼一個房間就是一個連通子圖;而這道題求得就是最大連通子圖的個數,以及其中點最多的最大連通子圖的頂點個數。
其次,題目中說用一個數字表示方塊周圍的牆,1表示西牆,2表示北牆,4表示東牆,8表示南牆。每個方塊用代表其周圍牆的數字之和表示。我們知道1,2,4,8的二進制表示分別是0001,0010,0100,1000.也就是說這些數字的和(有哪些牆的組合)有一個規律:相應位置上的二進制爲1,那麼就表示有對應的方向的牆。比如13這個數字的二進制是1101,那麼就表示有西牆、東牆和南牆。那麼我們用 & 就可以看是否有對應的牆。
知道哪些方向有牆後,就能遍歷整個圖,那麼我們用染色的思想,一個聯通子圖就染成一個顏色(用數字表示),最後就可以通過看有幾種顏色,每種顏色有多少種來得到答案了。
AC代碼
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int x,y; //x,y表示南北向,東西向的方塊數
int map[55][55]; //用map存放城堡的地圖
int color[55][55]; //用color數組來表示每個點是否訪問過以及分組
int area,maxArea = 0; //表示當前子圖的面積以及最大的面積
int num = 0; //房間數
void dfs(int i,int j)
{
//若已經訪問過
if(color[i][j] != 0)
return;
area++; //該房間可走且未走過,所以面積++
color[i][j] = num; //讓改點的color爲num房間號,染色
//然後訪問與i,j相鄰的所有可以訪問點
if((map[i][j] & 1) == 0) dfs(i,j-1); //西邊無牆
if((map[i][j] & 2) == 0) dfs(i-1,j); //北邊無牆
if((map[i][j] & 4) == 0) dfs(i,j+1); //東邊無牆
if((map[i][j] & 8) == 0) dfs(i+1,j); //南邊無牆
}
int main()
{
scanf("%d %d",&x,&y);
for(int i = 0;i < x; i++)
for(int j = 0;j < y; j++)
scanf("%d",&map[i][j]);
memset(color,0,sizeof(color)); //標記所有點爲新點
for(int i = 0;i < x; i++)
{
for(int j = 0;j < y; j++)
{
if(color[i][j] == 0)
{
/*當該點未被訪問過,那麼說明一定是一個新的聯通子圖,
房間數++;*/
num ++;
area = 0; //當前房間面積設置爲0
dfs(i,j);
if(maxArea<area)
maxArea = area;
}
}
}
//遍歷完圖,房間數和最大面積也就得出
printf("%d\n",num);
printf("%d\n",maxArea);
return 0;
}
例題練習
題目2:踩方格 OpenJ_Bailian - 4103
描述:
有一個方格矩陣,矩陣邊界在無窮遠處。我們做如下假設:
a. 每走一步時,只能從當前方格移動一格,走到某個相鄰的方格上;
b. 走過的格子立即塌陷無法再走第二次;
c. 只能向北、東、西三個方向走;
請問:如果允許在方格矩陣上走n步,共有多少種不同的方案。2種走法只要有一步不一樣,即被認爲是不同的方案。輸入:
允許在方格上行走的步數n(n <= 20)輸出:
計算出的方案數量樣例輸入:
2
樣例輸出:
7
思路分析:
首先根據題目,我們可以得出是要我們求出第i,j個位置走n步有多少種走法,其中只能向三個方向走
然後其實這個題也是用到了深度優先搜索,首先我們的起始狀態就是(X0,Y0,N),表示我們在第X0行第X0列上,還要走n步的不同走法。我們的結束狀態是(Xi,Yi,0),也就是到了某一個位置,還要走0步。我們要求得就是從初始狀態到結束狀態總共有多少種方法,其中每一個狀態(Xi,Yi,Ni)就代表一個點。
而(Xi, Yi, Ni) = (Xi + 1, Yi, n - 1) + (Xi, Yi - 1, n - 1) + (Xi, Yi + 1, n - 1); 即從一個狀態可以到其他三種狀態,而該狀態的值就是其他三種狀態的值的和。
AC代碼
#include<iostream>
#include<cstdio>
using namespace std;
int num = 0; //方案數
//確定map的大小 因爲最多一個方向連走n步(n<=20) 所以上下25,左右50(從中間開始)
int map[25][50]={0};
int N; //要走多少步
int dfs(int i,int j,int n)
{
//邊界條件 當要走的是0步時,那就是不走這一種方案
if(n == 0)
return 1;
map[i][j] = 1; //表示現在要訪問這一個點
/*接下來是狀態的轉移
之前在遞歸函數開頭判斷該點是否可走 現在是可走再遞歸*/
int x = 0;
if(map[i+1][j] == 0) x += dfs(i + 1, j, n - 1);
if(map[i][j+1] == 0) x += dfs(i, j + 1, n - 1);
if(map[i][j-1] == 0) x += dfs(i, j - 1, n - 1);
/*把map[i][j]重新置爲0,因爲表示不要這一步不要訪問這個點了
要退回去 讓這一步變成訪問其他的點*/
map[i][j] = 0;
return x;
}
int main()
{
scanf("%d",&N);
//表示從第0行,第25列走n步的總共走法
printf("%d",dfs(0,25,N));
return 0;
}