DFS模板 洛谷 P2089 P1605 P1101

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 烤雞

思路

i=09ai=10(1) \sum_{i=0}^{9} a_{i} = 10 \quad\quad\quad(1)
其中,
1ai3 1 \leq a_{i} \leq 3
試求滿足(1)式的方案數。

方案數可達到 10310^3 數量級,因此記錄結果的數組要開到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標記(x,y)(x, y)是否被訪問過(被標記)。其中,flag數組元素爲1代表該座標爲牆壁,無法通行;vis數組元素爲1代表該點已被訪問(標記)。

注意

對於以下dfs模板,

            爲進一步搜索所需要的狀態打上標記;
            search(t+1);
            恢復到打標記前的狀態;//也就是說的{回溯一步}

套用方法爲:
對於當前點(x,y)(x, y),利用x+=dx[i]x += dx[i]y+=dy[i](i=0,1,2,3)y += dy[i] (i = 0, 1, 2, 3)更新下一步搜索的座標,並在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數組存儲n×nn \times n單詞方針
  • 利用vis數組記錄word數組中對應座標(x,y)(x, y)是否在某一方向參與構成字符串"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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章