【回溯法】用子集樹和排序樹解決n後問題

回溯法

在問題的解空間樹中,按深度優先策略,根節點出發搜索解空間樹,算法搜索至解空間的任意節點時,先判斷該節點是否包含問題的解,若肯定不包含,則跳過對以該節點爲根的子樹的搜索,逐層向其祖先結點回溯。否則進入該子樹,繼續按根深度優先搜索。

子集樹和排列樹的區別

書上寫的

  • 當所給問題是從n個元素的集合S中找出滿足某種性質的子集時,相應的解空間樹稱爲子集樹。子集樹的時間複雜度爲2^n
  • 當所給的問題時確定n個元素滿足某種性質的排列時,相應的解空間樹成爲排列樹。排列樹的時間複雜度爲n!

我自己的概括

  • 子集樹的思想爲深度優先算法,從根節點出發,不斷訪問符合條件的子節點,(如果不符合,則進行剪枝並訪問該結點的兄弟節點),當訪問到葉節點的時候,進行輸出或者計數等操作,然後對葉節點的兄弟結點進行訪問,直到訪問結束,再訪問葉節點的根節點的兄弟結點(回溯)。
  • 排列樹也是從根節點觸發,不斷訪問符合條件的子節點,但是排列樹的t值和子集樹的t值不一樣,子集樹的t值可以代表每一個作業,而排列樹的t值僅僅可以代表作業的順序。
// n後問題
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <math.h>
#define M 100
int sum = 0;
int ok(int k,int *x) {
	for (int j= 1; j < k; j++) {
		if ((abs(k - j) == abs(x[j] - x[k]))||(x[j]==x[k])) {
			return 0;
		}
	}
	return 1;
}
void BackTrack(int t,int *x,int n) {
	if (t > n) {
		sum++;
		for (int i = 1; i <= n; i++) {
			printf("%d", x[i]);
		}
		printf("\n");
	}
	else
		for (int i = 1; i <= n; i++) {
			x[t] = i;
			if (ok(t,x)) BackTrack(t + 1,x,n);
		}
}
int main()
{
	int n;
	scanf("%d", &n);
	int x[M];
	for (int i = 0; i <= n; i++){
		x[i] = 0;
	}
	BackTrack(1, x, n);
	printf("%d", sum);
}


排列樹
排列樹是我利用了排列樹的思想,在子集樹的基礎上修改得來的

把排列樹改成子集樹,有幾點需要注意:

  1. 子集樹求得是集合,所以初始化集合的時候,把他們都賦值爲0,表示都沒有放東西,然後在回溯的時候,會將他們一一賦值。而排列樹求的是排列,所以我們要把需要被排列的值作爲初值賦給數組。
  2. 子集樹在從根節點到子節點的過程中,每個結點的取值都有n中可能性,我們爲了不重複,需要通過合法性判斷這個子集有沒有重複,而排列樹不需要這麼做,排列樹選完一個值之後,就會從剩下的值中繼續挑選,這樣我們就不需要判斷有沒有重複了。
// n後問題
//
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <math.h>
#define M 100
int sum = 0;
int ok(int k,int *x) {
	for (int j= 1; j < k; j++) {
		if ((abs(k - j) == abs(x[j] - x[k])) {
			return 0;
		}
	}
	return 1;
}
void swap(int* x, int t, int i) {
	int q = x[t];
	x[t] = x[i];
	x[i] = q;
}
void BackTrack(int t,int *x,int n) {
	if (t > n) {
		sum++;
		for (int i = 1; i <= n; i++) {
			printf("%d", x[i]);
		}
		printf("\n");
	}
	else
		for (int i = t; i <= n; i++) {
			swap(x,t, i);
			if (ok(t,x)) BackTrack(t + 1,x,n);
			swap(x,t, i);
		}
}
int main()
{
	int n;
	scanf("%d", &n);
	int x[M];
	for (int i = 0; i <= n; i++){
		x[i] = i;
		//初始化排列
	}
	BackTrack(1, x, n);
	printf("%d", sum);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章