标签:
林炳文Evankaka原创作品。转载请注明出处http://blog.csdn.net/evankaka
摘要:本文主要讲了n皇后问题的解题思路,并分别用java和c++实现了过程,最后,对于算法改进,使用了位运算。
一、问题抛出与初步解题思路
问题描述:八皇后问题是一个以国际象棋为背景的问题:如何能够在 8×8 的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上。
![技术分享](http://img.blog.csdn.net/20150927101018761)
转化规则:其实八皇后问题可以推广为更一般的n皇后摆放问题:这时棋盘的大小变为n×n,而皇后个数也变成n。当且仅当 n = 1 或 n ≥ 4 时问题有解。令一个一位数组a[n]保存所得解,其中a[i] 表示把第i个皇后放在第i行的列数(注意i的值都是从0开始计算的),下面就八皇后问题做一个简单的从规则到问题提取过程。
(1)因为所有的皇后都不能放在同一列,因此数组的不能存在相同的两个值。
(2)所有的皇后都不能在对角线上,那么该如何检测两个皇后是否在同一个对角线上?我们将棋盘的方格成一个二维数组,如下:
假设有两个皇后被放置在(i,j)和(k,l)的位置上,明显,当且仅当|i-k|=|j-l| 时,两个皇后才在同一条对角线上。
二、代码与结果
(1)C++版本
运行平台:VS2013
操作系统:Windows7
-
- #include <iostream>
- #include <cmath>
- #include<time.h>
-
- using namespace std;
- static int num;
- static int *x;
- static int sum = 0;
-
-
- bool place( int k )
- {
- for ( int j = 1; j < k; j++ )
- {
-
- if ( abs( x[k] - x[j] ) == abs( k - j ) || x[j] == x[k] )
- {
- return(false);
- }
- }
- return(true);
- }
-
-
- void backtrack( int t )
- {
- if ( t > num )
- {
- sum++;
-
- for ( int m = 1; m <= num; m++ )
- {
-
-
- for(int k =1; k <= num;k++){
- if(k == x[m]){
- cout << x[m] <<" ";
- }else {
- cout << "* ";
- }
- }
- cout << endl;
-
- }
- cout << endl;
- } else {
- for ( int i = 1; i <= num; i++ )
- {
- x[t] = i;
- if ( place( t ) )
- {
-
- backtrack( t + 1 );
- }
- }
- }
- }
-
-
- int main()
- {
- cout<<"请输入皇后数目:";
- cin>>num;
-
- clock_t start,finish;
- double totaltime;
- start=clock();
-
- x = new int[num + 1];
- for ( int i = 0; i <= num; i++ )
- x[i] = 0;
- backtrack( 1 );
- cout << "方案共有" << sum;
- delete[]x;
-
- finish=clock();
- totaltime=(double)(finish-start)/CLOCKS_PER_SEC;
- cout<<"\n此程序的运行时间为"<<totaltime<<"秒!"<<endl;
-
- while (1);
- return(0);
- }
输出结果:
8皇后:
![技术分享](http://img.blog.csdn.net/20150927103238845)
10皇后:
![技术分享](http://img.blog.csdn.net/20150927103247138)
(2)java版本
运行平台:eclispse luna
操作系统:Windows7
- package com.lin;
-
- import java.lang.*;
-
- public class QueenTest {
-
- public int[] x;
-
- public int queenNum;
-
- public int methodNum;
-
- QueenTest(int queenNum) {
- this.queenNum = queenNum;
- this.x = new int[queenNum+1];
- backtrack(1);
- }
-
-
- public void backtrack(int t)
- {
- if( t > queenNum)
- {
- methodNum++;
-
- for(int m = 1; m <= queenNum; m++){
-
-
- for(int k =1; k <= queenNum;k++){
- if(k == x[m]){
- System.out.print(x[m]+" ");
- }else {
- System.out.print("* ");
- }
- }
- System.out.println();
- }
- System.out.println();
- }
- else{
- for(int i = 1;i <= queenNum;i++)
- {
- x[t] = i;
- if(place(t)) {
- backtrack(t+1);
- }
- }
- }
- }
-
-
-
-
- public boolean place(int k) {
- for (int j = 1; j < k; j++)
-
- if (Math.abs(x[k] - x[j]) == Math.abs(k - j) || (x[j] == x[k])){
- return false;
- }
- return true;
- }
-
- public static void main(String[] args) {
- QueenTest queenTest = new QueenTest(8);
- System.out.println("总共解数为:"+ queenTest.methodNum);
-
- }
- }
输出结果:
这是八皇后
![技术分享](http://img.blog.csdn.net/20150927104155978)
这是十皇后:
通过对比java和C++发现,反而java运行更加快?这是为什么呢?原因就是C++中使用了new操作,而java中基本数据都是在栈上来创建的,存取的速度比堆快多了。
三、更加高效的算法-位运算版本
上面的方法递归次数实在太多了,也浪费空间,下面介绍目前号称是最快的--位运算。原理就不介绍了,看这里吧http://blog.csdn.net/xadillax/article/details/6512318
(1)Java代码
- package com.lin;
-
- import java.util.Scanner;
-
- public class QueenTest3 {
-
-
- public long sum = 0;
-
-
- public long upperlim = 1;
-
-
-
- void queenPos(long row, long ld, long rd)
- {
- if (row != upperlim)
- {
-
-
-
- long pos = upperlim & ~(row | ld | rd);
- while (pos != 0)
- {
-
-
- long p = pos & -pos;
-
-
-
-
- pos -= p;
-
-
-
-
-
-
-
-
- queenPos(row + p, (ld + p) << 1, (rd + p) >> 1);
- }
- }
- else
- {
-
- sum++;
- }
- }
-
-
- void queen(int queenNum) {
- if ((queenNum < 1) || (queenNum > 32)) {
- System.out.println(" 只能计算1-32之间\n");
- return;
- }
-
- upperlim = (upperlim << queenNum) - 1;
- queenPos(0, 0, 0);
- }
-
-
- public static void main(String[] args) {
- Scanner sc=new Scanner(System.in);
- System.out.print("请输入皇后数目:");
- int num=sc.nextInt();
- long starTime=System.currentTimeMillis();
- QueenTest3 queenTest3 = new QueenTest3();
- queenTest3.queen(num);
- System.out.println("总共解数为:"+ queenTest3.sum);
-
- long endTime=System.currentTimeMillis();
- double runTimes=(double)(endTime-starTime) / 1000.0;
- System.out.println("程序总共运行时间:"+ runTimes + "s");
-
-
- }
- }
运行结果:
八皇后的效果:(位运算版本)
![技术分享](http://img.blog.csdn.net/20150927201235963)
把上面的代码中的输出结果的去掉:(非位运算版本)
然后输出如下:
![技术分享](http://img.blog.csdn.net/20150927201811332)
经过两者对比,发现快了2ms
十皇后效果,没想到反而比八皇后的位运算版本还快(十皇后位运算版本)
![技术分享](http://img.blog.csdn.net/20150927201333428)
十皇后非位运算版本
快了10倍啊!!!!!!!!!!!!!!!!!!!
12皇后
位运算
![技术分享](http://img.blog.csdn.net/20150927204022428)
非位运算
![技术分享](http://img.blog.csdn.net/20150927204029853)
(2)C++版本
- #include <iostream>
- using namespace std;
- #include <time.h>
-
- long sum = 0, upperlim = 1;
-
- void test(long row, long ld, long rd)
- {
- if (row != upperlim)
- {
-
-
-
- long pos = upperlim & ~(row | ld | rd);
- while (pos)
- {
-
-
- long p = pos & -pos;
-
-
-
-
- pos -= p;
-
-
-
-
-
-
-
-
- test(row + p, (ld + p) << 1, (rd + p) >> 1);
- }
- }
- else
- {
-
- sum++;
- }
- }
-
- int main()
- {
- int num;
- cout<<"请输入皇后数目:";
- cin>>num;
-
- clock_t start,finish;
- double totaltime;
- start=clock();
-
-
-
-
- if ((num < 1) || (num > 32))
- {
- cout << " 只能计算1-32之间\n";
- return 0;
- }
-
-
- upperlim = (upperlim << num) - 1;
-
- test(0, 0, 0);
- cout << "方案共有" << sum;
-
- finish=clock();
- totaltime=(double)(finish-start)/CLOCKS_PER_SEC;
- cout<<"\n此程序的运行时间为"<<totaltime<<"秒!"<<endl;
- while(1);
- return 0;
- }
输出结果:
![技术分享](http://img.blog.csdn.net/20150927205441765)
![技术分享](http://img.blog.csdn.net/20150927205452562)
![技术分享](http://img.blog.csdn.net/20150927205500738)
下面来对比下java和C++运算的效果:
16皇后C++版本(位运算)
![技术分享](http://img.blog.csdn.net/20150927205531855)
16皇后java版本(位运算)
![技术分享](http://img.blog.csdn.net/20150927205921888)
发现又是java快了点。
from: http://blog.csdn.net/evankaka/article/details/48756951
n皇后2种解题思路与代码-Java与C++实现
标签:
原文地址:http://www.cnblogs.com/GarfieldEr007/p/5746270.html