算导4(8皇后-回溯问题)

来源:互联网 发布:linux重启后进不去系统 编辑:程序博客网 时间:2024/06/07 06:27
Solve the8-Queenproblem using back-trackingalgorithm.
解题思路:
   这道题目可以使用回溯算法。这种方法的思想是:为了求得问题的解,先选择某一种可能情况向前探索,如果发现是错误的,则退回一步重新选择,继续向前探索,如此反复进行,直至得到解或者证明无解。要解决N皇后问题,其实就是要解决好怎么放置这n个皇后,每一个皇后与前面的所有皇后不能在同一行、同一列、同一对角线,在这里我们可以以行优先,就是说皇后的行号按顺序递增,只考虑第i个皇后放置在第i行的哪一列,所以在放置第i个皇后的时候,可以从第1列判断起,如果可以放置在第1个位置,则跳到下一行放置下一个皇后。如果不能,则跳到下一列...直到最后一列,如果最后一列也不能放置,则说明此时放置方法出错,则回到上一个皇后向之前放置的下一列重新放置。此即是回溯法的精髓所在。当第n个皇后放置成功后,即得到一个可行解,此时再回到上一个皇后重新放置寻找下一个可行解...如此后,即可找出一个n皇后问题的所有可行解。 
复杂度分析:
    
复杂度小于n^3。棋盘是n行n列的,但是在每列选择可否放置的比较上又做了一次循环。但不是循环到n,而是根据皇后i的取值而变化的。所以复杂度应该是1/3n^3左右。

这是迭代的方法:
#include <cstdlib>
#include <iostream>
#include <math.h>
#define N 8
int x[N+1];//放置的列数
//int final[N+1][N+1];
using namespace std;
void print(int x[])
{
     int i= 1;
    for(int u = 1; u <= N; u++)
    {
           for(int v = 1; v <= N; v++)
           {
                  if(u == i&& v == x[i])
                      cout << "*"<< " ";
                  else 
                      cout << 0<< " ";
           }
           i++;
           cout << endl;
    }
     cout<< endl;
    //memset(final, 0,sizeof(final));//这个绝对不能加,每次memset要浪费很多时间 
     
}
bool Place(int k)
{
     int i= 1;
    while(i < k)
    {
           if(x[i] == x[k] || abs(x[i] - x[k]) == abs(i -k))
//如果实在同一对角线上,则他们横坐标的差值和纵坐标的差值绝对值相同。
               returnfalse;
           else i = i + 1;
    }
     returntrue;
int NQuees(int num)
{
     x[1] =0;
     int k= 1; 
    while(k > 0)
    {
           x[k] = x[k] + 1;
           while(x[k] <= N&& !Place(k))
           {
                     x[k] =x[k] + 1;
//不断搜寻第k个皇后应该放的位置,直到找到合适的或者此行搜索完毕也没有发现合适的位置。
           }
           if(x[k] <= N)//搜索到合适的位置
           {
                  if(k ==N)//如果对于第n个皇后已经搜索到,则可以打印x[k]
                  {
                      num++;
                      cout << "第"<< num<<"种:"<< endl;
                      print(x);
                      
                  }
                 else//如果没有到达最后一行,则行数k加1。并且从第一列开始尝试。 
                  {
                      k = k + 1;
                      x[k] = 0;
                  }    
           }
           else k = k -1;//不能搜索到合适的位置则说明上面的步骤有问题,则回溯到上一行重新比对。 
    }
     returnnum;
}
int main(int argc, char *argv[])
{
    int num = 0;
    cout<< "num:"<< NQuees(num)<< endl;
    system("PAUSE");
    returnEXIT_SUCCESS;
}
这是递归的方法:
#include <cstdlib>
#include <iostream>
#define N 8
using namespace std;
int final[N+1][N+1];
int x[N+1];
int sum = 0;
void print()
{
    for(int u = 1; u <= N; u++)
    {
           for(int v = 1; v <= N; v++)
           {
                  if(v == x[u])
                      cout << "*"<< " ";
                  else 
                      cout << "0"<< " ";
           }
           cout << endl;
    }
           
}
bool place(int k)
{
    for(int i = 1; i< k; i++)
    {
           if(x[k] ==x[i] || abs(x[k] - x[i]) == abs(k - i))
                 return false;
    }
    return true;
}
    
int quees(int k)
{
     if(k> N)
    {
         sum++;
         cout<< "第"<< sum<< "种:"<<endl; 
         print();
         cout<< endl;
         
    }
    else
    {
        for(int i = 1; i<= N; i++)
        {
               x[k] =i;
              if(place(k))
               {
                     quees(k+1);
              
        }
    }
     returnsum;
}
           
int main(int argc, char *argv[])
{
   quees(1); 
    cout<< sum<< endl;
    system("PAUSE");
    returnEXIT_SUCCESS;
}
      迭代回溯和递归回溯差不多。递归回溯存在一个潜在的问题,即当n增大时,递归的复杂度也将是几何级的增长,可能出现重复的情况。回溯法的解就是展开一个树。比如说这个N皇后问题,好像当n>60的时候,回溯法就不能完全地解决问题了,这时可以考虑用概率算法来解决,它可以解决很大的基数,只不过结果不是很精确而已。