N皇后——递归和非递归

来源:互联网 发布:万能小偷网站源码 编辑:程序博客网 时间:2024/05/17 03:37

总结: 8皇后的解有92种,但由于对称性,真正独立的解是12种

 

其中一种方案只能衍生出4个答案的情况,   
    
所以不重复的方案是12个,其中一个是对称图形,最终结果是11*8+1*4=92。  
   
  那个特殊方案就是:  
   
  .   .   X   .   .   .   .   .  
  .   .   .   .   X   .   .   .  
  .   X   .   .   .   .   .   .  
  .   .   .   .   .   .   .   X  
  X   .   .   .   .   .   .   .  
  .   .   .   .   .   .   X   .  
  .   .   .   X   .   .   .   .  
  .   .   .   .   .   X   .   .  

1.递归解

 

(1)

 

#include <stdio.h>
#include <stdlib.h>

const int N=20;   //最多放皇后的个数
int q[N];         //各皇后所在的行号
int cont = 0;     //统计解得个数
//输出一个解
void print(int n)
{
 cont++;
 printf("第%d个解:",cont);
 for(int i=1;i<=n;i++)
  printf("%d",q[i]);
 printf("/n");
 for(i=0;i<n;i++)       //col
 {               
  for(int j=0;j<n;j++) //row
  {
   if(q[j+1]!=i+1)
    printf("x ");
   else
    printf("Q ");
  }
  printf("/n");
 }
}
//检验第k列的i行上是否可以摆放皇后
int find(int i,int k) 
{
 int j=1;
 while(j<k)  //j=1~k-1是已经放置了皇后的列
 {
  //第j列的皇后是否在i列或(q[j],j)与(i,k)是否是同对角线
  if(q[j]==i||abs(j-k)==abs(q[j]-i))
   return 0;
  j++;
 }
 return 1;
}
//放置第k个皇后到第k列上
void place(int k,int n) 
{
 if(k>n)
  print(n);
 else
  for(int i=1;i<=n;i++)
   if(find(i,k))
   {
    q[k] = i;
    place(k+1,n);  //递归总是在成功完成了上次的任务的时候才做下一个任务
   }
}

int main()
{
 int n;
 printf("请输入皇后的个数(n<=20),n=:");
 scanf("%d",&n);
 if(n>20)
  printf("n值太大,不能求解!/n");
 else
 {
  printf("%d皇后问题求解如下(每列的皇后所在的行数):/n",n);
  place(1,n);        //问题从最初状态解起
  printf("/n");
 }
 return 1;
}

 

(2)

#include <stdio.h>
#include <stdlib.h>

int result = 1;
int chess[8];

// 函数:打印结果
void show_chess(void)
{
 int i;
 printf("Result - %d/n", result);
 for (i = 1; i <= 8; i++)
 {
  printf("(%d): %d/n", i, chess[i]);
 }
 ++result;
}


// 根据前面几行的子,检查这一行所放的子是否合法
int check( int n )
{
 int i;
 for (i = 1; i <= n - 1; i++)
 {
  if (chess[n] == chess[i] + (n - i) ||
   chess[n] == chess[i] - (n - i) ||
   chess[n] == chess[i] )
   return 0;
 }
 return 1;
}

// 递归函数:放子
void putchess( int n )
{
 int i;
 if (n <= 8)
 {
  for (i = 1; i <= 8; i++)  // 将第n行从第一格(i)开始往下放
  {
   chess[n] = i;
   if (check(n) == 1) // 若可放,则检查是否放满
   {
    if (n == 8)
     show_chess(); // 若已放满到8行时,则表示找出一种解,打印出来
    else
     putchess(n + 1); // 若没放满则放下一行 putchess(n+1)
   }
  }
 }
}


// 主程序
int main()
{
 printf("This is for 8 X 8 matrix./n");
 putchess(1); // 从每一行开始放子
 
 return 0;
}

 

 

 

2.非递归解

 

(1)

 

#include <iostream.h>
#define QUEEN 8    //皇后数量
int queen[QUEEN] ; //下标代表所在列号,值代表所在行号,
//如queen[1]=2表示第1列第2行有个皇后
bool col_YN[QUEEN] ;          //棋局的每一行是否有棋,有则为1,无为0 ;
bool passive_YN[2*QUEEN-1] ;  //斜率为1的斜线方向上是否有棋,共有2*QUEEN-1个斜线
bool negative_YN[2*QUEEN-1] ; //斜率为负1的斜线方向上是否有棋
//用全局变量,因全局数组元素值自动为0
int main()
{
 int col = 0 ;//游标,当前移动的棋子(以列计)
 bool flag = false ;   //当前棋子位置是否合法
 queen[0] = -1 ;      //第0列棋子准备,因一开始移动的就是第0列棋子
 int count = 0 ;      //一共有多少种解法的计数器 ;
 
 while(col>=0 ) //跳出条件是回溯到无法回溯时
 {
        queen[col]++ ;      //col列上的皇后走到下一行试试
        if(queen[col] >= QUEEN) //当前列全部走完
        {
   queen[col] = -1 ; //当前列棋子置于准备状态
   col-- ;        //回溯到上一列的棋子
   if(col>=0)      //回溯时要清理如下行,斜线的标志位  
   {
    col_YN[queen[col]] = false ;
    passive_YN[queen[col] + col] = false ;
    negative_YN[QUEEN-1 + col - queen[col]] = false ;
   }
  }
        else
        {
   //先判断棋子所在行没有棋子
   if(col_YN[queen[col]] == false)
   {
    flag = true ;
    //以下检查当前棋子是否与之前的棋子斜线相交
    if( passive_YN[queen[col] + col] == true || negative_YN[QUEEN-1 + col -      queen[col]] == true)
     flag = false ;
    else    
     flag = true ;
    if(flag) // flag为真表示位置合法
    {
     if(col == QUEEN-1) //列到达最后,即最后一个皇后也找到位置,输出解
     {
      count++ ; //解法的数目加一 ;
      cout<<"***第"<<count<<"种解法***"<<endl ;
      for(int i=0;i<QUEEN;i++)
       cout<<"第"<<i<<"列皇后在第"<<queen[ i ]<<"行"<<endl;
     }
     col_YN[queen[col]] = true ;// 当前行设为有棋子
     passive_YN[queen[col] + col] = true ;//当前行正斜率方向有棋子
     negative_YN[QUEEN-1 + col - queen[col]] = true ; //当前行负斜率方向                   上也有棋子
     col++ ;
     if(col >= QUEEN)
     { // 找到解后再次回溯找另外的解,这同上面无解回溯是一样的
      col-- ;
      col_YN[queen[col]] = false ;
      passive_YN[queen[col] + col] = false ;
      negative_YN[QUEEN-1 + col - queen[col]] = false ;//原理同回溯
     }     
     flag = false ;    
    }
   }
  }
 }
 cout<<QUEEN<<"皇后问题一共有"<<count<<"种解法"<<endl ;
 return 0 ;
}

 

 

(2)利用栈模拟

 

#include<iostream>
#include<iomanip>
#include<cmath>
#include<stack>
using namespace std;

//利用递归求解皇后问题 x[i]表示皇后放在第i行第x[i]列
static int count;

//判断如果皇后放在第i行,第j列是否与前面的皇后冲突
bool place(int i,int j,int* path)//path存放路径
{
 int row;
 for(row=0;row<i;row++) 
  if(j==path[row]||abs(row-i)==abs(path[row]-j))//在同一列列或同一条斜线上
   return false;
 return true;
}

//利用栈来模拟递归,在某个扩展节点出处,将所有符合条件的节点加入到里面
struct pos
{
 int row;
 int col;
};
//找到当前最合适的节点,如果没有找到则返回-1
int find_col(int row,int col,int* path,int n)
{
 int j;
 for(j=col;j<n;j++)
 {
  if(place(row,j,path))
   return j;
 }
 if(j==n)
  return -1;
}
//利用栈来模拟八皇后问题
void stack_stimu(int n,int* path)
{
 stack<struct pos> s;
 int currow=0;
 int flag=0;
 //主要结构分为两部分,第一 按照正常顺序寻找节点
 //然后找出回溯的情况:在八皇后问题中主要有两中:1.到达结尾 找出路径 2.当前行没有满足条件的位置
 while(true)
 {
  if(currow<n)
  {
   int col=find_col(currow,0,path,n);
   if(col!=-1)
   {
    pos node;
    node.row=currow;
    node.col=col;
    s.push(node);
    path[currow]=col;
    currow++;
   }
   else
    flag=1;
  }
  else
  {
   for(int i=0;i<n;i++)
    cout<<setw(5)<<left<<path[i]+1;
   cout<<endl;
   count++;
   flag=1;
  }
  // 进行回溯
  if(flag==1)
  {
   //描述了回溯的过程
   while(!s.empty())
   {
    pos temp=s.top();
    if(temp.col!=n-1)     //由temp.col!=7改为temp.col!=n-1
    {
     //查找当前最适合的节点,并入栈
     int j=find_col(temp.row,temp.col+1,path,n);
     if(j!=-1)
     {
      pos node;
      node.row=temp.row;
      node.col=j;
      s.pop();
      s.push(node);
      path[temp.row]=j;
      currow=temp.row+1;
      flag=0;
      break;
     }
     else
      s.pop();
    }
    else
     s.pop();
            }
   if(s.empty())
    return;//函数的出口处
        }
 }
}

int main()
{
 cout<<"Queen Place Problem:"<<endl;
 cout<<"Input the value of n"<<endl;
 int n;
 cout<<"  n>";
 cin>>n;
 int* path=new int[n];
 
 //初始化
 for(int i=0;i<n;i++)
  path[i]=-1;
  stack_stimu(n,path);  //利用栈来模拟八皇后问题
  cout<<"the count is: "<<count<<endl;
 return 0;
}

 

 

(3)

 

#include<stdio.h>
#define NUM 8 /*定义数组的大小*/
int a[NUM+1];
int main()
{
 int i,k,flag,not_finish=1,count=0;
 i=1; /*正在处理的元素下标,表示前i-1个元素已符合要求,正在处理第i个元素*/
 a[1]=1; /*为数组的第一个元素赋初值*/
 printf("The possible configuration of 8 queens are:/n");
 while(not_finish) /*not_finish=1:处理尚未结束*/
 {
  while(not_finish&&i<=NUM) /*处理尚未结束且还没处理到第NUM个元素*/
  {
   for(flag=1,k=1;flag&&k<i;k++) /*判断是否有多个皇后在同一行*/
    if(a[k]==a[i])flag=0;
    for(k=1;flag&&k<i;k++) /*判断是否有多个皇后在同一对角线*/
     if((a[i]==a[k]-(k-i))||(a[i]==a[k]+(k-i))) flag=0;
     if(!flag) /*若存在矛盾不满足要求,需要重新设置第i个元素*/
     {
      if(a[i]==a[i-1]) /*若a[i]的值已经经过一圈追上a[i-1]的值*/
      {
       i--; /*退回一步,重新试探处理前一个元素*/
       if(i>1&&a[i]==NUM)
        a[i]=1; /*当a[i]为NUM时将a[i]的值置1*/
       else if(i==1&&a[i]==NUM)
        not_finish=0; /*当第一位的值达到NUM时结束*/
       else a[i]++; /*将a[i]的值取下一个值*/
      }
      else if(a[i]==NUM) a[i]=1;
      else a[i]++; /*将a[i]的值取下一个值*/
     }
     else if(++i<=NUM)
      if(a[i-1]==NUM) a[i]=1; /*若前一个元素的值为NUM则a[i]=1*/
      else a[i]=a[i-1]+1; /*否则元素的值为前一个元素的下一个值*/
  }
  if(not_finish)
  {
   ++count;
   printf((count-1)%3?" [%2d]: ":" /n[%2d]: ",count);
   for(k=1;k<=NUM;k++) /*输出结果*/
    printf(" %d",a[k]);
   if(a[NUM-1]<NUM) a[NUM-1]++; /*修改倒数第二位的值*/
   else a[NUM-1]=1;
   i=NUM-1; /*开始寻找下一个足条件的解*/
  }
 }
}

原创粉丝点击