DFS模板
int search(int t)
{
if(滿足輸出條件)
{
輸出解;
return;
}
else
{
for(int i=1;i<=嘗試方法數;i++)
if(滿足進一步搜索條件)
{
爲進一步搜索所需要的狀態打上標記;
search(t+1);
恢復到打標記前的狀態;//也就是說的{回溯一步}
}
}
}
注意
1.第一個if是符合輸出解的條件,第二個if是符合進一步搜索的條件;
2.下一步搜索時,不是使用return search(t+1),而是直接search(t+1);(新手可能會注意不到這個關鍵的地方,以至於每次寫完不知道爲什麼只得到一個答案就返回主程序了)
3.for循環之後的if可以是多個;
4.for循環邊界,例如:
-
方向是四個,那麼邊界肯定就是4;(帖主用3,是因爲從0開始的)
-
素數環需要嘗試1至20,那麼邊界就是20;
例題1
洛谷 P2089 烤雞
思路
其中,
試求滿足(1)式的方案數。
坑
方案數可達到 數量級,因此記錄結果的數組要開到10000
C++代碼如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
#include <string>
#include <vector>
using namespace std;
const int maxn = 10000;
int a[maxn][15];
int mem[10];
int cnt;
int n;
void dfs(int t, int total)
{
if (t == 10) // 滿足輸出條件
{
// 輸出解
if (total == n)
{
for (int j = 0; j < 10; j++){
a[cnt][j] = mem[j];
}
cnt++;
}
return;
}
else
{
for (int i = 1; i <= 3; i++)
{
if (total <= n) // 滿足進一步搜索的條件
{
mem[t] = i; // 爲進一步搜索所需要的狀態打上標記;
dfs(t + 1, total + i);
mem[t] = 0; // 恢復到打標記前的狀態, 也就是說的{回溯一步}
}
}
}
}
int main()
{
cin >> n;
cnt = 0;
memset(a, 0, sizeof(a));
memset(mem, 0, sizeof(mem));
dfs(0, 0);
cout << cnt << endl;
for (int i = 0; i < cnt; i++){
for (int j = 0; j < 10; j++){
cout << a[i][j] << " ";
}
cout << endl;
}
return 0;
}
例題2
洛谷 P1605 迷宮
思路
- 對座標進行dfs,return條件爲達到終點,每次到達終點方案數++
- 利用數組dx和dy模擬四個方向(上下左右)移動的座標變化
- 利用二維數組flag記錄牆壁;利用二維數組vis標記是否被訪問過(被標記)。其中,flag數組元素爲1代表該座標爲牆壁,無法通行;vis數組元素爲1代表該點已被訪問(標記)。
注意
對於以下dfs模板,
爲進一步搜索所需要的狀態打上標記;
search(t+1);
恢復到打標記前的狀態;//也就是說的{回溯一步}
套用方法爲:
對於當前點,利用和更新下一步搜索的座標,並在dfs前將vis[x][y]標記爲1,代表本輪已經搜索過次座標。在dfs後恢復標記,即將vis[x][y]重新置爲0。
C++代碼如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
#include <string>
#include <vector>
using namespace std;
int cnt;
int sx, sy, fx, fy;
int n, m, t;
const int maxn = 10;
int vis[maxn][maxn]; // 1代表被標記
int flag[maxn][maxn]; // 1代表牆壁
int dx[] = {0, 0, -1, 1}; // 上下左右4個方向的橫座標變化
int dy[] = {1, -1, 0, 0}; // 上下左右4個方向的縱座標變化
void dfs(int x, int y)
{
if(x == fx && y == fy){ // 到達迷宮終點(fx, fy)
cnt++; // 方案數++
}
else
{
for (int i = 0; i < 4; i++) // 嘗試上下左右4個方向
{
vis[x][y] = 1; // 標記已被訪問
int new_x = x + dx[i], new_y = y + dy[i]; // 更新進一步嘗試搜索的座標
if (!flag[new_x][new_y] && new_x >= 1 && new_y >= 1 && new_x <= n && new_y <= m && !vis[new_x][new_y])
{
dfs(new_x, new_y); // 進一步搜索
}
vis[x][y] = 0; // 恢復標記
}
}
}
int main()
{
cnt = 0; // 初始化方案數爲0
memset(flag, 0, sizeof(flag));
memset(vis, 0, sizeof(vis));
cin >> n >> m >> t;
cin >> sx >> sy >> fx >> fy;
for (int i = 0; i < t; i++){
int xx, yy;
cin >> xx >> yy;
flag[xx][yy] = 1; // 牆
}
dfs(sx, sy); // 從起點開始搜索
cout << cnt << endl;
return 0;
}
例題3
洛谷 P1101 單詞方陣
思路
- 對座標進行dfs,利用數組dx和dy記錄8個擴展方向。
- 利用w結構體數組記錄每輪"yizhong"字符串的搜索路徑
- 利用word數組存儲單詞方針
- 利用vis數組記錄word數組中對應座標是否在某一方向參與構成字符串"yizhong"
- dfs的剪枝(繼續搜索)條件爲:已搜索7個字符("yizhong"字符串的字符個數)或當前字符的下一個字符和其在"yizhong"的下一個字符相同。
- 在main函數中遍歷整個字符方針,當出現’y’字符時,遍歷8個方向,當僅當下一個字符爲’i’時從’y’字符的座標開始dfs,將結果記錄於vis二維數組。
- 輸出答案:若vis數組對應座標被標記,則輸出原單詞方陣對應位置的字符;否則,輸出’*’。
注意
本次dfs由於題目要求構成字符串"yizhong"的字母可以共享,故而不需要在遞歸前進行標記,亦無須恢復標記。
C++代碼如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
#include <string>
#include <vector>
using namespace std;
int dx[] = {0, 0, -1, 1, -1, -1, 1, 1}; // 8個方向的橫座標變化
int dy[] = {1, -1, 0, 0, 1, -1, 1, -1}; // 8個方向的縱座標變化
const int maxn = 105;
struct node
{
int x, y;
};
node w[maxn]; // 保存字符串(x, y)路徑
char word[maxn][maxn]; // 單詞矩陣
char stand[] = "yizhong"; // 匹配字符串
int vis[maxn][maxn]; // ans
void dfs(int t, int k, int x, int y){
if (t == 7){
for (int i = 0; i < 7; i++){
vis[w[i].x][w[i].y] = 1;
}
}
else
{
int new_x = x + dx[k]; // 更新x座標
int new_y = y + dy[k]; // 更新y座標
if (t == 6 || word[new_x][new_y] == stand[t + 1]){ // 滿足繼續搜索條件
w[t].x = x; w[t].y = y; // 記錄字符串路徑座標
dfs(t + 1, k, new_x, new_y); // 遞歸
}
}
}
int main()
{
int n; cin >> n;
memset(word, 0, sizeof(word));
memset(vis, 0, sizeof(word));
memset(w, 0, sizeof(w));
for (int i = 0; i < n; i++){
for (int j = 0; j < n; j++){
cin >> word[i][j];
}
}
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < n; ++j)
{
if (word[i][j] == 'y'){ // 滿足搜索條件1
for (int k = 0; k < 7; k++){
int x = i + dx[k], y = j + dy[k];
if (word[x][y] == 'i'){ // 滿足繼續搜索條件2
dfs(0, k, i, j);
}
}
}
}
}
for (int i = 0; i < n; i++){ // 輸出答案
for (int j = 0; j < n; j++){
if (vis[i][j] == 1){
cout << word[i][j];
}
else{
cout << '*';
}
}
cout << endl;
}
return 0;
}