n皇后问题

来源:互联网 发布:有哪些句子知乎 编辑:程序博客网 时间:2024/06/05 10:36
/*  Name: n皇后问题  Copyright:   Author: 巧若拙   Date: 07-03-17 09:09  Description: 一、问题描述:在n×n格的棋盘上放置彼此不受攻击的n个皇后。按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。n后问题等价于再n×n的棋盘上放置n个皇后,任何2个皇后不放在同一行或同一列或同一斜线上。输入:    给定棋盘的大小n (n ≤ 13)输出:    输出有多少种放置方法。二、解题思路:要解决N皇后问题,其实就是要解决好怎么放置这n个皇后,每一个皇后与前面的所有皇后不能在同一行、同一列、同一对角线,在这里我们可以以行优先,就是说皇后的行号按顺序递增,只考虑第i个皇后放置在第i行的哪一列,所以在放置第i个皇后的时候,可以从第1列判断起,如果可以放置在第1个位置,则跳到下一行放置下一个皇后。如果不能,则跳到下一列...直到最后一列,如果最后一列也不能放置,则说明此时放置方法出错,则回到上一个皇后向之前放置的下一列重新放置。此即是回溯法的精髓所在。当第n个皇后放置成功后,即得到一个可行解,此时再回到上一个皇后重新放置寻找下一个可行解...如此后,即可找出一个n皇后问题的所有可行解。三、复杂度分析:每一行都有n个位置可以放置,一共有n^n中可能性。因而最坏情况下时间复杂度为O(n^n)。似乎比穷举法要高,但是由于回溯法不测试死结点的分支,它的平均时间复杂度要低于穷举法。 */#include<iostream>#include<cmath>using namespace std;const int N = 4; //皇后的个数int c[N];//记录n个皇后的列坐标 int sum = 0;//保存可以放置的方案数bool OK(int t);//检查当前皇后的列坐标是否合法 void Backtrace(int t); //递归回溯 void Backtrace_2(); //非递归回溯 int main() {   Backtrace(0);  // Backtrace_2();      system("pause");   return 0;}bool OK(int t)//检查当前皇后的列坐标是否合法 {    for(int i=0; i<t; i++)    {        if(c[i] == c[t] || t-i == abs(c[t]-c[i]))      return false;           }    return true;}void Backtrace(int t) //递归回溯 {    if(t == N)    {        sum++;        cout << "方案" << sum << ": ";        for(int i=0; i<N; i++)        {            cout << c[i] << " ";        }        cout << endl;    }    else    {        for(int i=1; i<=N; i++)        {c[t] = i;            if(OK(t))Backtrace(t+1);        }      //  c[t] = 0;   //列坐标还原并回溯(由于c[t]每次都从0开始取值,故列坐标无需还原)    }}void Backtrace_2() //非递归回溯 {int t = 0; //第一个顶点入栈while(t >= 0){while(c[t]++ < N)//c[t]默认初始化为0 {if (OK(t)) //c[t]可行才进入下一步 {if(t == N-1){sum++;cout << "方案" << sum << ": ";for(int i=0; i<N; i++){cout << c[i] << " ";}cout << endl;}else{t++;}}}c[t--] = 0; //列坐标还原并回溯}}
/*  Name: n皇后问题  Copyright:   Author: 巧若拙   Date: 07-03-17 09:09  Description: 一、问题描述:在n×n格的棋盘上放置彼此不受攻击的n个皇后。按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。n后问题等价于再n×n的棋盘上放置n个皇后,任何2个皇后不放在同一行或同一列或同一斜线上。输入:    给定棋盘的大小n (n ≤ 13)输出:    输出有多少种放置方法。二、解题思路:要解决N皇后问题,其实就是要解决好怎么放置这n个皇后,每一个皇后与前面的所有皇后不能在同一行或同一列或同一斜线上,在这里我们可以以行优先,就是说皇后的行号按顺序递增,只考虑第i个皇后放置在第i行的哪一列,所以在放置第i个皇后的时候,可以从第1列判断起,如果可以放置在第1个位置,则跳到下一行放置下一个皇后。如果不能,则跳到下一列...直到最后一列,如果最后一列也不能放置,则说明之前的皇后放置有问题,则回溯到上一个皇后,把其换到下一个可能位置。此即是回溯法的精髓所在。当第n个皇后放置成功后,即得到一个可行解,此时再回到上一个皇后重新放置寻找下一个可行解...如此后,即可找出一个n皇后问题的所有可行解。本算法的精妙之处在于分别使用三个数组 b[N],c[N+N],d[N+N]用来存储该列和两条斜线是否可用,这样就无需使用函数来判断该坐标是否可用了。 尤其为了避免数组c的下标越界,使用了c[i-j+N]表示坐标的处理,甚为高妙。 */#include<iostream>#include<cmath>using namespace std;const int N = 8; //皇后的个数int cel[N];//记录n个皇后的列坐标 bool b[N]; //b[j]==0表示列j可用bool c[N+N]; //c[j]==0表示左上-右下斜线可用, i - cel[i] == j - cel[j]bool d[N+N]; //d[j]==0表示右上-左下斜线可用  i + cel[i] == j + cel[j]int sum = 0;//保存可以放置的方案数void Backtrace(int r); //递归回溯,r表示第r行 void Backtrace_2(int r);int main() {  // Backtrace(0);   Backtrace_2(0);      return 0;}void Backtrace(int r) //递归回溯,r表示第r行 {    if(r == N)    {        sum++;        cout << "方案" << sum << ": ";        for(int i=0; i<N; i++)        {            cout << cel[i]+1 << " ";        }        cout << endl;    }    else    {        for(int j=0; j<N; j++)//可能的列号         {if((!b[j])&& (!c[r-j+N]) && (!d[r+j])) //c[i-j+N]是为了确保下标不越界 {cel[r] = j;b[j] = 1;   //宣布占领列j c[r-j+N] = 1; //宣布占领两条斜线 d[r+j] = 1;Backtrace(r+1); //继续递归放置下一个皇后 //还原,以便回溯 b[j] = 0;  c[r-j+N] = 0; d[r+j] = 0;}        }    }}void Backtrace_2(int r) //递归回溯,r表示第r行 {    for(int j=0; j<N; j++)//可能的列号     {if((!b[j])&& (!c[r-j+N]) && (!d[r+j])) //c[i-j+N]是为了确保下标不越界 {cel[r] = j;b[j] = 1;   //宣布占领列j c[r-j+N] = 1; //宣布占领两条斜线 d[r+j] = 1;if(r == N-1)    {    sum++;    cout << "方案" << sum << ": ";    for(int i=0; i<N; i++)    {        cout << cel[i]+1 << " ";    }    cout << endl;}elseBacktrace_2(r+1); //继续递归放置下一个皇后 //还原,以便回溯 b[j] = 0;  c[r-j+N] = 0; d[r+j] = 0;}    }}



0 0