题目就不多说了,昨天要交个作业,今天把想到的都记录一下;
递归回溯算法一:
#include<iostream>
#include<cstdlib>
#define N 100
using namespace std;
int board[N];
int n,ans = 0;
void Trial(int i,int n);
bool Judge(int i,int j) {
if(j > n || i > n)return false;
for(int l = 1; l < i; l++) {
if(board[l] == j) return false;
if(board[l] + i == board[i] + l || board[l] + l == board[i] + i) return false;
}
return true;
}
void print() {
for(int i = 1; i <= n; i++) {
cout << board[i] << " ";
}
cout << endl;
ans ++;
}
void GoBack(int i,int n) {
if(i < 1) {
cout << n <<"皇后";
cout << "一共有" << ans << "种解法" << endl;
exit(0);
};
board[i]++;
while(!Judge(i,board[i]) && board[i] <= n)board[i]++;
if(board[i] <= n) Trial(i+1,n);
else GoBack(i - 1,n);
}
void Trial(int i,int n) {
if(i > n) print();
else {
for(int j = 1; j <=n; j++) {
board[i] = j;
if(Judge(i,j)) Trial(i+1,n);
}
GoBack(i - 1,n);
}
}
int main() {
cin >> n;
for(int i = 1; i <= n; i++)
board[i] = 0;
Trial(1,n);
return 0;
}
这是第一个想到的,里面有两个函数的相互递归调用,运行的时候发现当n= 9的时候就开始爆栈了,应该是递归调用太多了;改进了一下
递归算法二:
#include<iostream>
#include<cstdlib>
#define N 100
using namespace std;
int board[N];
int n,ans = 0;
bool Judge(int i,int j) {
if(j > n || i > n)return false;
for(int l = 1; l < i; l++) {
if(board[l] == j) return false;
if(board[l] + i == board[i] + l || board[l] + l == board[i] + i) return false;
}
return true;
}
void print() {
for(int i = 1; i <= n; i++) {
cout << board[i] << " ";
}
cout << endl;
ans ++;
}
void Trial(int i,int n) {
if(i > n) print();
else {
for(int j = 1; j <=n; j++) {
board[i] = j;
if(Judge(i,j)) Trial(i+1,n);
}
while(i > 1) {
i--;
board[i]++;
while(!Judge(i,board[i]) && board[i] <= n)board[i]++;
if(board[i] <= n) Trial(i+1,n);
}
cout << n <<"皇后";
cout << "一共有" << ans << "种解法" << endl;
exit(0);
}
}
int main() {
cin >> n;
for(int i = 1; i <= n; i++)
board[i] = 0;
Trial(1,n);
return 0;
}
这个就是把GoBack函数去掉了变成了一个函数的递归,结果也没找到哪去,n= 9 是可以算了,10的时候一样的爆栈,最后终究还是改成了非递归;
非递归回溯算法:
#include<iostream>
#include<cstdlib>
#include<time.h>
#define N 100
using namespace std;
int board[N];//存储第n行的棋子放在第几列
int n;
long long ans;
time_t Tstart,Tend;
bool Judge(int i,int j) {//判断i,j处放棋子是否合法
if(j > n || i > n)return false;
for(int l = 1; l < i; l++) {
if(board[l] == j) return false;
if(board[l] + i == board[i] + l || board[l] + l == board[i] + i) return false;
}
return true;
}
void print() {//输出棋子分布
for(int i = 1; i <= n; i++) {
cout << board[i] << " ";
}
cout << endl;
ans ++;
}
void Trial(int i,int n) {//非递归回溯搜索
int j = 1;
while(i <= n+1) {//寻找当前第i行的位置
if(j > n || i > n ) {//当行或者列超出棋盘时说明上一个棋子应该重新变换位置
if(i > n ) print();//当行超出棋盘时说明当前已经产生一种输出
while(i > 1) {//回溯
i--;
board[i]++;//改变位置
while(!Judge(i,board[i]) && board[i] <= n)board[i]++;//循环寻找下一个可行位置直到找到了或者全部找完
if(board[i] <= n) {//如果找到了则退出循环
i++;
break;;//此处break会跳到最外层while循环里
}
}
if(i == 1) return;//经过while循环回溯后若当i=1的时候说明解已经全部找完了
}
if(i <= n) {//寻找当前棋子位置
for( j = 1; j <=n; j++) {
board[i] = j;
if(Judge(i,j)) {//如果当前位置可行则寻找下一个位置
i++;
break;//此处break会跳到最外层while循环里
}
}
}
}
}
int main() {
ios::sync_with_stdio(false); //关闭输出流同步
cout << "请输入皇后个数:" << endl;
while(cin >> n) {
Tstart = time(NULL);
ans = 0;
for(int i = 1; i <= n; i++)
board[i] = 0;
Trial(1,n);
cout << n <<"皇后";
cout << "一共有" << ans << "种解法" << endl;
Tend = time(NULL);
cout << "用时:" << Tend - Tstart << "秒" << endl;
cout << "请输入皇后个数:" << endl;
}
return 0;
}
改成非递归后就不会有爆栈的情况发生 了,n的值只要不超过棋盘范围或者结果不溢出longlong就能算,就是时间有点慢,n>15的时候就要等很久了;
另外把输出流同步关了可以省掉很多时间;
递归变非递归真的难,纠结了好久逻辑都乱了T.T。
----------------------------------------------日期分割线--------------------------------------------------
刚刚在做数独的题目的时候回头看了一下最后这个代码发现关键代码中居然有部分代码功能重复了,我就说这个题怎么这么会复杂。。。
到底还是自己先想清楚再写好啊。。附上修改后的Trial()函数
void Trial(int i,int n) {//非递归回溯搜索
int j = 1;
while(i <= n+1 && i > 0) {//寻找当前第i行的位置,当i= 0时说明全部解搜索完毕
if(i > n ) {print();i--;}//当行超出棋盘时说明当前已经产生一种输出,输出当前解后回溯继续产生下一个解
if(i > 0) {
board[i] ++;
while(!Judge(i,board[i]) && board[i] <= n)board[i]++;//循环寻找下一个可行位置直到找到了或者全部找完
if(board[i] <= n) {//如果找到了则退出循环
//cout << i<<" --" << board[i] << endl;
i++;
}else{//回溯
board[i] = 0;//回退的时候记得将当前点状态复原
i--;
}
}
}
}
代码一下就少了十几行啊!!果然逻辑混乱是不出好代码的。-_-|||