ACM:回溯法,八皇后问题,素数环

(一)八皇后问题
(1)回溯法

#include <iostream>
#include <string>

#define MAXN 100

using namespace std;

int tot = 0, n = 8;
int C[MAXN];

void search(int cur) {
	if(cur == n) ++tot;       //递归边界,只要走到了这里,所有皇后必然不冲突
	else for(int i = 0; i < n; ++i) {
		int ok = 1;
		C[cur] = i;     //尝试把第cur行的皇后放在第i列
		for(int j = 0; j < cur; ++j) {     //检查是否和前面的皇后冲突
			if(C[cur] == C[j] || cur-C[cur] == j-C[j] || cur+C[cur] == j+C[j]) {
				ok = 0;
				break;
			}
		}
		if(ok)  search(cur+1);     //如果合法,则继续递归
	}
}

int main() {
	search(0);
	cout << tot << endl;
	return 0;
}

(2)利用二维数组优化的回溯法

#include <iostream>
#include <string>

#define MAXN 100

using namespace std;

int tot = 0, n = 8;
int vis[3][MAXN], C[MAXN];    

void search(int cur) {
	if(cur == n) ++tot;
	else for(int i = 0; i < n; ++i) {
		if(!vis[0][i] && !vis[1][cur+i] && !vis[2][cur-i+n]) {    //利用二维数组直接判断
			C[cur] = i;
			vis[0][i] = vis[1][cur+i] = vis[2][cur-i+n] = 1;   //修改全局变量
			search(cur+1);
			vis[0][i] = vis[1][cur+i] = vis[2][cur-i+n] = 0;   //这里一定要改回来!
		}
	}
}

int main() {
	memset(vis, 0, sizeof(vis));
	search(0);
	cout << tot << endl;
	return 0;
}

在上面的程序中,vis数组表示已经放置的皇后占据了哪些列、主对角线和副对角线。

一般的在回溯法中,如果修改了全局变量vis数组,那么递归调用结束后一定要修改回来!因为在解答树中,如果下一层不满足条件,那么就需要回溯,那么就要把修改过的vis给改回来,那样,才能继续进行下一次的判断!!!

(二)素数环

题目:输入正整数n,把整数1,2,3,…,n组成一个环,使得相邻两个整数之和均为素数。输出时从整数1开始逆时针排列。同一个环应该恰好输出一次。

典型的回溯法,代码如下:

#include <iostream>
#include <algorithm>
using namespace std;

const int MAXN = 1000;
int isp[MAXN], vis[MAXN], A[MAXN], n;

int is_prime(int x) {    //判断一个数是否为素数
	for(int i = 2; i*i <= x; ++i) {
		if(x % i == 0) return 0;
	}
	return 1;
}

void dfs(int cur) {
	if(cur == n && isp[A[0] + A[n-1]]) {
		for(int i = 0; i < n; ++i) cout << A[i] << " ";
		cout << endl;
	}else {
		for(int i = 2; i <= n; ++i) {
			if(!vis[i] && isp[i + A[cur-1]]) {
				A[cur] = i;   //数字i满足条件,所以第cur个位置可以放数字i
				vis[i] = 1;
				dfs(cur+1);
				vis[i] = 0;   //跟上题一样,一定不能忘记把vis的值改回来,原因见上一题的代码注释
			}
		}
	}
}

int main() {
	memset(vis, 0, sizeof(vis));   //递归调用之前,一定要把vis函数清0
	cin >> n;
	for(int i = 2; i <= 2*n; ++i) isp[i] = is_prime(i);   //先判断一个数是不是素数,便于后期判断
	A[0] = 1;   //题目规定第一个数字从1开始
	dfs(1);     //所以递归调用是用位置1开始,而不是从位置0开始,因为第一个位置的数字已经确定了是1
	return 0;
}

    原文作者:回溯法
    原文地址: https://blog.csdn.net/u010470972/article/details/36708243
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞