1. 素数环
问题描述:把若干正整数填到一个环中,要求每个整数只填写一次,且相邻两个整数之和为素数。
应用回溯法思想(也就是继续DFS递归),注意递归边界条件和冲突条件是什么。
素数环问题还需特别注意对A[0]的处理。因为第一次PrimeCircle(1)要比较i和A[index-1]加和是否为素数,所以A[0]初始值不应赋为0。
(我太蠢了 数组下标和递归算法的参数和小循环的i值(也就是放的数字)老容易搞混,记住:永远不会出现A[i]的写法。
#include <iostream>
#include <math.h>
using namespace std;
int Prime(int x){
//判断x是否为素数
int i,n;
n=(int)sqrt(x);
for(i=2;i<=n;i++){
if(x%i==0) return 0;
}
return 1;
}
//n=6的素数环问题
int n=6;
int A[6]={1,0,0,0,0,0};//用来放素数的素数环数组
/*这里的A[0]不能为0,否则对PrimeCircle(1)会有越界错误*/
bool vis[4]={0};//访问标记数组
//递归关键弄清index和i的含义。index是大环上的指向标记,i是小循环的
void PrimeCircle(int index){
//递归边界条件,判断第一个数和最后一个数加和
if(index==n+1 && Prime(A[1]+A[n])){
for(int i=1;i<=n;i++)
cout << A[i] << " ";
cout << "\n";
}
for(int i=1;i<=n;i++){
if(vis[i]==false && Prime(i+A[index-1])){
//冲突条件:i没有访问过且与前面一个数加和为素数
A[index]=i;
vis[i]=true;
PrimeCircle(index+1);
vis[i]=false;
}
}
}
int main()
{
PrimeCircle(1);
return 0;
}
代码运行结果(n值为6的素数环)
2. 八皇后
问题描述:在8×8棋盘上放8枚棋子,使其互相不在一条线上(不同行,不同列,不同斜线)。
同样的DFS思想,与素数环不同的是八枚棋子是相同的,不需要设置vis数组记录位置是否访问过。可以用“把棋子放上去”、“取下来的思路”作为标记访问。也就是说,再递归调用后需要重新把棋子放下——对应数组元素设为0。
我的思路是用二维数组表示棋盘,放棋子用1,没有放棋子用0,输出打印所有解法。
另外check(line,list) 函数也需要注意。斜线比较时,(line(和 li)- i)、(line(和 li)+ i) 范围应在0~9之间,也就是要控制一下斜线下标的范围,如果没有这个限制很多时候返回false了(比如第一行第一个棋子的左上方,==1才会false,不为1自然就true了)。我一开始这里没有注意界限,还是找大佬debug被点明后才意识到这里竟然对结果有影响。
八皇后看着容易,实际短短几十行代码调了许久,期间遇到无数不知道什么问题,非常感谢大佬帮忙,终于搞出来了。卑微的跨考生真的代码这一块太需要练习了。
#include <iostream>
#include <cmath>
using namespace std;
int n=8;
int sum;//解的个数
int Queens[9][9]={0};//怕越界数组定的大一点
//bool vis[9]={0}; 无须标记访问,放棋子的过程就是标记
bool check(int line,int li){
//line为行,list为列
//遍历该行之前的所有行
for (int i=0; i<line; i++) {
//如果在同一列,该位置不能放
if (Queens[i][li]==1) return false;
}
for(int i=0;i<li;i++){
//如果在同一行,该位置不能放
if(Queens[line][i]==1) return false;
}
for(int i=0;i<line;i++){
//斜线上(左上)不能放
//这里要注意:line-i与li-i的范围应在0~9之间。
if((line-i)>0 && (li-i)>0 && Queens[line-i][li-i]==1) return false;
}
for(int i=0;i<line;i++){
//斜线上(右上)不能放
if((line-i)>0 && (li+i)<9 && Queens[line-i][li+i]==1) return false;
}
//如果以上情况都不是,当前位置就可以放皇后
return true;
}
void Queen(int line){
if(line == n+1){
sum++;//解个数增加
//打印输出此解
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cout << Queens[i][j]<<" ";
}
cout <<"\n";
}
cout <<"\n";
}
for(int li=1;li<=n;li++){
if(check(line,li)==true){
Queens[line][li]=1;
//vis[li]=true;
Queen(line+1);
//vis[li]=false;
//拿掉棋子
/*这里拿掉棋子和取消标记是类似的意思,
八皇后放下一枚棋子的过程只是“试“,而不是真正的放上去。
试了之后访问标记要置回否,放的棋子要取下来*/
Queens[line][li]=0;
}
}
}
int main()
{
Queen(1);
cout << "摆放方式有:" << sum << "种";
return 0;
}
运行结果: