N皇后问题

问题描述:实现n皇后问题,要求利用概率算法和回溯法;只需找出一组解即可。

程序设计:

  1 #include<iostream>
  2 #include<math.h>
  3 #include <stdlib.h>
  4 #include <ctime>
  5 using namespace std;
  6 int x[100],q[100];//q[i]=n表示回溯解,x表示概率解
  7 int n,cnt = 0;
  8 clock_t start1, finish1;
  9 clock_t start2, finish2;
 10 double totaltime1,totaltime2;
 11 //回溯放置皇后时判断是否合法
 12 bool find(int a[],int k)
 13 {
 14     for(int i = 1;i < k;i++)
 15     {
 16         if(a[i] == a[k] || abs(k-i) == abs(a[k] - a[i]))
 17             return false;
 18     }
 19     return true;
 20 }
 21 bool Place(int x[],int k)
 22 {
 23     for (int i = 0;i < k;i++)
 24         if(x[i] == x[k] || abs(i - k) == abs(x[i] - x[k]))
 25         {
 26             return true;
 27         }
 28     return false;
 29 }
 30 void print(int n)
 31 {
 32     int i,j;
 33     cnt++;
 34     
 35     cout << "" << cnt << " 个解" << endl;
 36     
 37     for(i = 1;i <= n;i++)
 38         cout <<"(" <<  i << "," << q[i] << ")" << " ";//第i行放在第q[i]处。
 39     cout << endl;
 40     
 41     for(i = 1;i <= n;i++)        //
 42     {
 43         for(j = 1;j <= n;j++)    //
 44         {
 45             if(q[i] != j)
 46                 printf("x ");
 47             else
 48                 printf("Q ");
 49         }
 50         cout << endl;
 51     }
 52 }
 53 //回溯法求n皇后
 54 void queen(int n){
 55     cnt = 0;
 56     for(int i = 0;i <= n;i++)//先将所有行的列数清零
 57     {
 58         q[i] = 0;
 59     }
 60     
 61     int k = 1;//k是行数
 62     while(k >= 1)
 63     {
 64         q[k] += 1;
 65         while(q[k] <= n )//未到达最后一列
 66         {
 67             if(find(q,k))
 68                 break;
 69             else
 70                 q[k]++;//该列不行向后推一个
 71         }
 72         if(k == n && q[k] <= n)//如果到最后一行,且最后列数正确
 73         {
 74             for(int i = 1;i <= n;i++)
 75             {
 76                 cout << q[i] << " ";
 77                 
 78             }
 79             cout << endl;
 80             print(n);//将皇后以棋盘式输出
 81         }
 82         else if(q[k] <= n)//如果还没有到达最后一列,继续下一列
 83         {
 84             k++;
 85         }
 86         else//回溯,因为到达最后一行,且列数遍历完,仍没有出结果
 87         {
 88             q[k] = 0;//让该行的列数重置为0
 89             k--;//继续回到上一行
 90             
 91         }
 92     }
 93 }
 94 //类,产生随机数
 95 class RandomNumber{//类,这样才能保证不会出现产生的随机值都一样的情况。
 96 public:
 97     RandomNumber(){
 98         srand(time(0));
 99     }
100     int get(int begin = 0, int end = 1){//用这个函数可以得到随机值
101         return rand()%(end-begin+1)+begin;
102     }
103 };
104 
105 
106 bool Queen(int n) //拉斯维加斯概率求解
107 {
108     RandomNumber arr[100];
109     
110     int i,j,count=0;// //定义i,j,count. 其中count为试探次数
111     for(i = 0;i < n;)// //其中 n为行数,for循环经过n次,每次代表在第n行中找到皇后所在的列
112     {
113             //为j赋值为一个随机数,此为拉斯维加斯概率算法的核心思想,通过随机性选择快速的引导算法
114         j = arr[i + 1].get(1,n);//这样才可以保证每次的随机数都不一样,如果用j=rand()%n+1;不可以
115         
116         x[i]=j;  //将随机选取的j作为列向量参与接下来的运算
117         count++;//试探次数加一,用于确定N次运算后n列全部实验完毕
118         if(!Place(x,i))   //这个if语句用于调用冲突子函数以确定是否发生冲突,即这个位置是否可以放置皇后
119         {
120             count=0;
121             i++;
122         }
123         else if(count==n)   //位置冲突
124         {
125             return false;
126       
127         }
128         
129     }
130     
131     cout << "拉斯维加斯概率求解" << n << "皇后的一个解为" << endl;
132     
133     for(i=0;i<n;i++)//  //作为输出结果时的for循环
134     {
135        cout <<"(" <<  i + 1 << "," << x[i] << ")" << " ";
136         
137     }
138     cout << endl;
139     return true;
140 }
141 void runtime()
142 {
143     totaltime1 = finish1 - start1;
144     totaltime2 = finish2 - start2;
145     cout << "回溯法求解"<< n << "皇后的运行时间为" << totaltime1 << "毫秒" << endl;
146     cout << "拉斯维加斯概率算法求解 "<< n << "皇后的运行时间为" << totaltime2  << "毫秒"<< endl;
147 }
148 int choose()
149 {
150     int ch;
151     cout << endl << endl << endl;
152     
153     cout << "-----------------------n皇后问题---------------------" << endl;
154     cout << "                请选择你要进行的操作                   " << endl;
155     cout << "   1、回溯法求解              2、拉斯维加斯概率算法求解   " << endl;
156     cout << "   3、两种算法运行时间比较    4、退出程序              " << endl;
157     cout << "------------------------------------------------------" << endl;
158     cin >> ch;
159     return ch;
160 }
161 int main()
162 {
163     int ch = choose();
164   
165     while(ch != 4)
166     {
167         
168         switch (ch) {
169             case 1:
170                 printf("请输入皇后的个数(n <= 32),n = :");
171                 cin >> n;
172                 start1 = clock();//开始计时
173                 queen(n);
174                 finish1 = clock();
175                 break;
176                 
177             case 2:
178                 printf("请输入皇后的个数(n <= 32),n = :");
179                 cin >> n;
180                 start2 = clock();//开始计时
181                 cout << "正在重复测试,请等待······" << endl;
182                 while(!Queen(n))
183                 {
184                 }
185                 finish2 = clock();
186                 break;
187             case 3:
188                 runtime();
189                 break;
190             default:
191                 exit(0);
192         }
193       
194         ch =  choose();
195  
196     }
197     return 0;
198 }

结果输出:

 

性能测试:

依次测试n的值在4-11范围内两种算法的程序运行时间,绘制图像如下图所示:

当n=12时,用回溯法求得共有14200个解,时间明显增大:

算法时间复杂度分析:

回溯法:递归n行,每次循环n列,比较0~n - 1次

n * n * (n - 1) / 2,即O(n ^ 3),O(n * n * (n - 1) / 2) = O(n ^ 3)

拉斯维加斯概率算法:求出一组解的时候,仅需判断每次n行,比较0 ~ n - 1次,即 n * (n - 1) / 2 = O(n ^ 2)

posted @ 2024-01-20 23:08  棒打鲜橙不加冰  阅读(11)  评论(0编辑  收藏  举报