深度优先搜索:例如走迷宫,你没有办法用分身术来站在每个走过的位置,不撞南山不回头,利用栈这种数据结构来实现(利用递归便于实现,但是效率较低),找到的第一个解不一定是最优解,只是先序遍历最早的可行解。
回溯法试探法:按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。在现实中,有很多问题往往需要我们把其所有可能穷举出来,然后从中找出满足某种要求的可能或最优的情况,从而得到整个问题的解。回溯算法就是解决这种问题的“通用算法”,有“万能算法”之称。
Problem Description
在N*N的方格棋盘放置了N个皇后,使得它们不相互攻击(即任意2个皇后不允许处在同一排,同一列,也不允许处在与棋盘边框成45角的斜线上。
你的任务是,对于给定的N,求出有多少种合法的放置方法。
Input
共有若干行,每行一个正整数N≤10,表示棋盘和皇后的数量;如果N=0,表示结束。
Output
共有若干行,每行一个正整数,表示对应输入行的皇后的不同放置数量。
Sample Input
1
8
5
0
Sample Output
1
92
10
思路:根据深搜的模板来嵌套即可,唯一需要注意的就是判断条件。以行为标准,则不用判断行,直接判断列与斜线即可。
代码即是把上述内容表述出来。
本题存在一个小坑,就是应该用数组存储N皇后的结果,然后打表输出。
#include<cstdio>
#include<cstrin>
using namespace std;
int map[12][12];
int cnt=0;
int n;
bool ok(int i,int y)
{
for(int j=0;j<=y;j++)
{//j描述行,因为一行行标点,所以只要判断前y点就好
if(map[j][i]==1)//判断同一列是否满足条件
return false;
if(y-j>=0&&i-j>=0)//防止越界
if(map[y-j][i-j]==1)//判断左上斜线
return false;
if(y-j>=0&&i+j<=n)//防止越界
if(map[y-j][i+j]==1)//判断右上斜线
return false;
}
return true;
}
void dfs(int y)
{
if(y==n){//结束递归条件
cnt++;
}
for(int x=0;x<n;x++){//x为列数,一列列填点。
if(ok(x,y)){//如果该点满足条件,填充。
map[y][x]=1;
dfs(y+1);
map[y][x]=0;//恢复棋盘。
}
}
}
int main()
{
int ans[11]={0};
for(n=1;n<=10;n++)
{
cnt=0;
dfs(0);//搜索从0行开始
ans[n]=cnt;
}
while(~scanf("%d",&n)&&n)
printf("%d\n",ans[n]);
return 0;
}
根据上述的思路,可以进行空间复杂的简化
即用一个x[]数组存储的列,而下标代表行,判断斜线上的即可用绝对值的差值即可,次算法优化了存储空间,但对时间复杂度无太大影响都为31MS
#include <cstdio>
#include <cmath>
#include <cstring>
#define N 15
using namespace std;
int x[N],result[N]; //皇后放置的列数
int n; //皇后个数
int sum=0; //可行解个数
int place(int k)
{
int i;
for(i=1;i<k;i++)
if(abs(k-i)==abs(x[k]-x[i])||x[k]==x[i])
return 0;
return 1;
}
int queen(int k)
{
int i;
if(k>n)
sum++;
else
for(i=1;i<=n;i++)
{
x[k]=i;
if(place(k))
queen(k+1);
}
return sum;
}
int main()
{
for(n=1;n<=10;n++)
{
result[n]=queen(1);
sum=0;
}
while(scanf("%d",&n)==1&&n!=0)
printf("%d\n",result[n]);
return 0;
}
判断斜线上是否存在皇后,还有一种数学技巧,左上右下或左下右上斜线上共有2*n-1种情况
#include <iostream>
#include <cstring>
using namespace std;
int n;
int ans=0;
bool a[20]; //列使用情况 true 用
bool x1[20]; //左上*右下的情况
bool y1[20]; //左下+右上的情况
void dfs(int deep)
{
if(deep>=n){
ans++;
return ;
}
for(int i=0;i<n;i++){
if(x1[i+deep]==false&&y1[i-deep+n]==false&&a[i]==false)
{
x[deep+i]=true;
y1[i-deep+n]=true;
a[i]=true;
dfs(deep+1);
x[deep+i]=false;
y1[i-deep+n]=false;
a[i]=false;
}
}
}