回溯/递归——八皇后

问题描述:在8X8的棋盘上放置八个皇后,任意两个皇后都不能处于同一行、列或者同一斜线上。
分析:每行只能放一个,每列只能放一个。

回溯

如何判段在当前位置是否可以防止皇后

行和列的判断很简单,主要是对角线的判断
在 / 对角线上的点:行 + 列相等
在 \ 对角线上的点:行 - 列相等
通过建立皇后对象的结构体,重载==实现两个皇后相等的判断

	bool operator==(const Queen& obj){
		return (x == obj.x || y == obj.y ||
		(x + y) == (obj.x + obj.y) ||
		(x - y) == (obj.x - obj.y));
		return x == obj.x;
	}

建立Queen表示皇后

包含:坐标,皇后相等的判断

struct Queen
{
	int x, y;
	Queen(int xx = 0, int yy = 0) :x(xx), y(yy){}
	bool operator==(const Queen& obj){
		return (x == obj.x || y == obj.y ||
		(x + y) == (obj.x + obj.y) ||
		(x - y) == (obj.x - obj.y));
		return x == obj.x;
	}
	bool operator!=(const Queen&obj){
		return *this == obj;
	}
	void operator=(const Queen&obj){
		x = obj.x;
		y = obj.y;
	}

};

思路

显而易见,每一行只能放一个皇后,因此我们选择一行一行的防止皇后。我们将可以的满足条件的Queen放入vector中。第一个皇后从(0,0)位置开始放置,并将其信息记录到vector中,然后进入下一行,从(1,0)开始通过在vector中find当前Queen(重载过==)来判断是否是合法位置,可以放置则入栈,不能则进入(1,1),循环进行。。。。

find函数

bool find(vector<Queen>v, Queen obj){
	int i = 0;
	for (i = v.size()-1; i >=0; i--){
		if (v[i] == obj)
			return true;;
	}
	return false;
}

直到这一行都没有位置可以放置的时候,说明上次放置的位置不合理,出vector中剔除最后一个元素(等价于出栈),回到剔除元素的位置,换下一个位置判断。。。

Queen pop(vector<Queen>&v){
	Queen q;
	q = v[v.size()-1];
	v.pop_back();
	return q;
}

当成功放置八个皇后后,同样出栈回到上次的位置,换下一个位置判断,这样就可以得到所有的可能。

void placeQueens(int N){
	vector<Queen>v;
	Queen q(0, 0);
	do
	{
		if (N <= v.size() || N <= q.y){//非法点 出栈
			//若size==N说明已经找出一组解了 把当前栈顶同样出栈  找下一组解
			q = pop(v);
			q.y++;
		}
		else
		{
			while ((q.y < N) && (find(v, q)))
退出while的可能:
	//1.q.y>=N跳出边界,(跳出边界前没有找到不相同的 ) 
	//出现这中情况则说明在y<N的范围内没有合法点 所以上一次的点是不合法的 会出栈


   //2.q.y<N在边界中合法,则只有后面的条件为false 即和前面的点都不同 
   // 这种点就是当前的合法点,入栈。
			{
				q.y++;
			}
			if (N>q.y){
				v.push_back(q);//记录当前合法点入栈信息x y
				q.x++;//行向下移动找下一行的合法点
				q.y = 0;
			}
		}
		if (v.size() >= N){
			print(v, N);
			//只有合法点才会入栈 一共有N个合法点 所以当 
			//size==N时说明栈中的点就是一组解 total++
			total++;
			cout << total << endl;
		}
	} 
	while (0<q.x || q.y<N);
	//更容易理解的循环条件是while (!(q.x == 0 && q.y == N));
}

递归

相对而言递归就简单的多,递归本质上相当于操作系统帮我们完成了入栈出栈的操作,因此我们只需要判断递归的开始条件和终止条件即可。

思路:

我们用一个int ret[9]的数组来记录皇后的位置,用数组下标表示皇后所在行,下标中对应元素表示列。

用函数void placeQueens2(int queennum)来实现目标,参数为已经放置的皇后个数,每成功找到一个皇后则在 ret中记录他的位置,然后
void placeQueens2(queennum+1)找下一个皇后

当下一个皇后找不到时 返回,并从ret中将ret[–queennum]的皇后信息清除(因为不可用),return递归终止条件1

当queennum = 7时说明8个皇后都找到了,递归终止条件2

int ret[8] = { -1 };
int q_total = 0;
bool avaiable(int num, int pos){
	for (int i = 0; i < num; ++i){
		if (ret[i] == pos || (i + ret[i] == num + pos) ||
		 (i - ret[i] == num - pos))
			return false;
	}
	return true;
}
void print(int ret[]){
	for (int i = 0; i < 8;i++){
		cout << ret[i] << " ";
	}
	cout << endl;
}
void placeQueens2(int queennum){
	for (int i = 0; i < 8; i++){
		if (avaiable(queennum, i)){
			ret[queennum] = i;
			if (queennum<7)
				placeQueens2(queennum + 1);
			else{
				q_total++;
				print(ret);
				break;//这里用break return 甚至什么都不用结果都一样
				//但是过程是不同的用break效率最高
			}
		}
	}
	ret[--queennum] = -1;//用--queennum也可以,因为只要对皇后个数--后,
	//其内容就已经失效了
	return;
}

发布了145 篇原创文章 · 获赞 12 · 访问量 9641

猜你喜欢

转载自blog.csdn.net/weixin_44997886/article/details/105036607
今日推荐