アルゴリズム予備シリーズ1-深さ優先探索の検索列挙

誰もが列挙アルゴリズムを学んだと思います。学んだら、Jun Jiaoから学んでください!

まずは質問を見てください〜

トピック1:完全な配置

  • 説明
    nを入力し、繰り返されない1からnまでのすべての順列、つまりnのすべての順列を出力します。
  • 分析
    明らかに、この問題はforループ列挙では実行できません。これは、「n-recycle」を使用する必要があり、nは変数であるため、列挙アルゴリズムを使用できないためです。
    この問題を解決する方法は?ループ層の数が不定の場合、再帰によって実現できると誰もが考える必要があります。これが、深さ優先探索(dfs)アルゴリズムの目的の1つです。
    再帰のプロセスでは、dfsアルゴリズムはforループを使用して列挙します。つまり、ここでのforループの層の数は、1つずつ書き込むのではなく、再帰によって決定されます。
  • コード
#include <bits/stdc++.h>
using namespace std;
bool vis[11]; // 假设n最大为10,vis[i]表示第i个数在当前这个排列中是否被用过
int n, a[11]; // 表当前排列的结果 
void dfs(int stp) {
    
    	// stp是step的缩写,表示现在已经枚举到了第几步(所谓的第几层循环) 
	if (stp == n + 1) {
    
    	// 当n层循环都枚举完后,输出当前结果并且结束dfs函数 
		for (int i = 1; i <= n; ++i) {
    
    
			cout << a[i] << ' ';
		}
		cout << '\n'; // 不要忘记排列之间要用换行隔开 
		return ;
	}
	for (int i = 1; i <= n; ++i) {
    
    	// 枚举所有数,找到符合条件的数(没有被用过),然后放到当前数位上 
		if (!vis[i]) {
    
    
			vis[i] = 1;	a[stp] = i; // 先放到当前数位上,继续枚举,然后尝试其他数,这样的过程就是一个回溯算法 
			dfs(stp + 1);
			vis[i] = 0;
		}
	}
}
int main() {
    
    
	cin >> n;
	dfs(1); // 从第1个数开始枚举 
	return 0;
}

コメントはすでに非常に明確であり、こんにゃく氏はこのトピックについてもう話しません。
こんにゃくジュンの暖かいリマインダー:検索と列挙のプロセス中に、質問の性質に応じてプロセスを整理して最適化できます(これについては次の数レッスンで説明します)。ただし、ほとんどの問題では、dfsの時間計算量が超過するため、検索する前に問題状態の総数を決定する必要があります。
みなさんへの練習用質問です〜

トピック2:ローストチキン

  • 背景
    豚ハンケは鶏肉を手に入れた
  • 説明
    豚ハンケはローストチキンを食べるのが好きです(これは同じ動物です、なぜ揚げるのが緊急すぎるのですか!)ハンケは鶏肉を非常に特別に食べます、なぜそれは特別なのですか?具材は10種類(マスタード、クミンなど)なので、具材1〜3グラムでローストチキンの美味しさは全具材の合計です。
    ハンケさんが教えてくれたら知りたくなります。美味しさ、これら10成分のマッチングスキームをすべて出力してください
  • 入力形式
    1行、n <= 5000
  • 出力形式
    1行目、プログラムの総数、
    2行目から最後まで、10個の数字。これは、各成分の品質が
    辞書式順序で配置されていることを示します。
    要件を満たす方法がない場合は、最初の行に「0」を出力するだけです。
  • 入力例#1
    11
  • 出力例#1
    10
    1 1 1 1 1 1 1 1 1 2
    1 1 1 1 1 1 1 1 2 1
    1 1 1 1 1 1 1 2 1 1
    1 1 1 1 1 1 2 1 1 1
    1 1 1 1 1 2 1 1 1 1 1
    1 1 1 1 2 1 1 1 1 1
    1 1 1 2 1 1 1 1 1 1
    1 1 2 1 1 1 1 1 1 1
    1 2 1 1 1 1 1 1 1 1
    2 1 1 1 1 1 1 1 1 1
  • 分析
    明らかに、この質問は10サイクルで列挙できますが、最も基本的なdfを学習しました。なぜ、10サイクルの列挙を使用するのでしょうか。Konjai Junがテンプレートを提供しますので、タイトルを書き終えてくださいここから送信できます
#include <bits/stdc++.h>
using namespace std;
int n, ans1, ans2[10000][10], arr[10], sum;
void dfs(int stp, int now) {
    
    
	// 请继续完成这个函数~ 
}

int main() {
    
    
	cin >> n; dfs(1, 0);
	cout << ans1 << '\n';
	for (int i = 1; i <= ans1; ++i) {
    
    
		for (int j = 1; j <= 10; ++j) {
    
    
			cout << ans2[i][j] << ' ';
		}
		puts("");
	}
	return 0;
}

  • 解釈
    タイトル効果は
    、n、10を出力するプログラムの種類数とnの数を入力し、順序は2種類とは見なされず、各オプションを出力します。
    問題解決のアイデア
    最初のレベル(最初の配列要素)から始めて、現在の配列の合計を記録します。dfsを下げ続け、最終的に臨界点に達したときに条件を判断し、条件に合った記録データと保存データを記録します。満足できない場合は、前のレベルに戻ります。
    コード
#include <bits/stdc++.h>
using namespace std;
int n, ans1, ans2[10000][10], arr[10], sum;
void dfs(int stp, int now) {
    
    
	if (stp > 10) {
    
    
		if (now == n) {
    
    
			++ans1;
			for (int i = 1; i <= 10; ++i) {
    
    
				ans2[ans1][i] = arr[i];
			}
		}
		return ;
	}
	for (int i = 1; i <= 3; ++i) {
    
    
		if (now + i > n) {
    
    
			break;
		}
		arr[stp] = i;
		dfs(stp + 1, now + i);
		arr[stp] = 0;
	} 
}
int main() {
    
    
	cin >> n; dfs(1, 0);
	cout << ans1 << '\n';
	for (int i = 1; i <= ans1; ++i) {
    
    
		for (int j = 1; j <= 10; ++j) {
    
    
			cout << ans2[i][j] << ' ';
		}
		puts("");
	}
	return 0;
}

これを学んだ後、みんなが小さなパターンを発見したはずです〜

みんなのための基本的なdfsフレームワークをリストしてください〜

void dfs(目前状态) {
    
    
	判断边界
	尝试每一种可能,跳出可以继续的 {
    
    
		继续搜索,尝试下一步 
	} 
}

もちろん、列挙はforループを使用する必要はありません!これがみんなの例です、みんなに理解してもらいたいです〜

トピック3:次数nの魔方陣

  • 問題
    nを入力し、n次の魔方陣の解を出力します。
  • 問題解決のアイデア
    魔方陣を学んだ友人は、ここでそれを見るのに「大根法」(同音異義語)を使用できない人のことを考えるでしょう
  • コード
#include <bits/stdc++.h>
using namespace std;
int n, a[45][45];
void dfs(int x, int y, int now) {
    
    	// 目前坐标和下一步要填的数
	if (now > n * n) {
    
    	// 所有数都填完了,输出,然后结束dfs函数 
		for (int i = 1; i <= n; ++i) {
    
    
			for (int j = 1; j <= n; ++j) {
    
    
				cout << a[i][j] << ' ';
			}
			cout << '\n';
		}
		return ;
	}
	// 直接模拟即可
	if (x == 1 && y != n) {
    
    
		a[n][y + 1] = now;
		dfs(n, y + 1, now + 1);
	} else if (y == n && x != 1) {
    
    
		a[x - 1][1] = now;
		dfs(x - 1, 1, now + 1);
	} else if (x == 1 && y == n) {
    
    
		a[x + 1][y] = now;
		dfs(x + 1, y, now + 1);
	} else {
    
    
		if (a[x - 1][y + 1] == 0) {
    
    
			a[x - 1][y + 1] = now;
			dfs(x - 1, y + 1, now + 1);
		} else {
    
    
			a[x + 1][y] = now;
			dfs(x + 1, y, now + 1);
		}
	}
}
int main() {
    
    
	cin >> n;
	a[1][(n + 1) / 2] = 1; // 特殊处理1 
	dfs(1, (n + 1) / 2, 2);
	return 0;
} 

これを見て、あなたはすでに基本的なdfsの問題に対処しましたか?役に立ったら、こんにゃくの第2章を楽しみにしていてください!

おすすめ

転載: blog.csdn.net/yueyuedog/article/details/113067647
おすすめ