【回溯法】用子集树和排序树解决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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章