都是老掉牙的东西了。写这篇只是整理下自己的思路。不知道八皇后问题的见 百度文库
分析1:
何为横向、纵向、斜向不冲突? 前两个很容易理解。斜向呢?翻译成数学就是任意两个皇后所在的位置所形成的线(两点一线)的斜率不能为1或者-1;
如何遍历整个棋盘? 最简单的就是八重循环来遍历每行。再在重循环中遍历改行的每一列。
模拟放置: 遍历第0行,先放置(0,0)位置 判断安全;
遍历第1行, 放置(1,0),不安全。 放置(1,1) ,还是不安全。 放置(1,2) ,安全;
遍历第2行。。。。。
何为安全、不安全? 判断是否安全时,都需要与已前面已放置的皇后进行条件判断。既然是已放置的皇后,那么肯定要选择存放皇后的数据结构。采用二维数组还是什么?由于每行只能放置一个皇后,我们只要知道某行、某列放置了皇后就OK。对应整数数组int[]已经足够,(即下标表示行,值表示列。 如Queen[row] =col ,表示row行col列 放置了皇后。)。 在a行,b列放置一枚皇后时,如果发现符合如下条件。都视为不安全。
1.不能为同行,因为数据结构能采用Int[] 的前提是不能为同行。免去判断。
2.不能为同列, Queen[row]=col 中的任意col=b;
3.不能为斜向, Queen[row]=col 中任意 (col-b)/(row-a)=1 或者 (col-b)/(row-a)=-1;
代码如下:
/// <summary>
/// 判断某行某列是否安全。
/// </summary>
/// <param name="a">行</param>
/// <param name="b">列</param>
/// <returns></returns>
public bool IsSafe(int a, int b)
{
for (int row = 0; row < a; row++)//遍历前面的行,后面的还没放。不需要遍历。
{
if (b == Queen[row])//同列
{
return false;
}
if (b - Queen[row] == a - row || b - Queen[row] == row - a)//斜列
{
return false;
}
}
return true;
}
放置函数:
public void PlaceQueen_For()
{
for (int col0 = 0; col0 < 8; col0++)//遍历第0行的每列
{
if (IsSafe(0, col0))//
{
Queen[0] = col0;//把安全位置存放起来,
for (int col1 = 0; col1 < 8; col1++)//遍历第1行的每列
{
if (IsSafe(1, col1))
{
Queen[1] = col1;
for (int col2 = 0; col2 < 8; col2++)
{
if (IsSafe(2, col2))
{
Queen[2] = col2;
for (int col3 = 0; col3 < 8; col3++)
{
if (IsSafe(3, col3))
{
Queen[3] = col3;
for (int col4 = 0; col4 < 8; col4++)
{
if (IsSafe(4, col4))
{
Queen[4] = col4;
for (int col5 = 0; col5 < 8; col5++)
{
if (IsSafe(5, col5))
{
Queen[5] = col5;
for (int col6 = 0; col6 < 8; col6++)
{
if (IsSafe(6, col6))
{
Queen[6] = col6;
for (int col7 = 0; col7 < 8; col7++)
{
if (IsSafe(7, col7))
{
Queen[7] = col7;
var a= (int[]) Queen.Clone();//克隆一份当前放置方法。
List.Add(a);//List 为List<int[]>结构 。用来存放成功解法的放置方法。
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
分析2:上面的解法虽然已经能解出答案。但是可扩展性相当差。 如果是10皇后、12皇后呢?再者,我们发现每层FOR循环非常类似。是否可以采用递归?
提取上面的循环代码如下:
/// <summary>
/// 某行放皇后。
/// </summary>
/// <param name="row">行</param>
public void PlaceQueen(int row)
{
if (row >= Size)//成功解法。已经放置了N个安全的皇后。(Size为初始时的N皇后)
{
//记录放置方法。
int[] cloneQueen = (int[])Queen.Clone();
List.Add(cloneQueen);
return;
}
for (int col = 0; col < Size; col++)//遍历每一列
{
if (IsSafe(row, col))
{
Queen[row] = col;
PlaceQueen(row + 1);
}
}
}
运行时运行 PlaceQueen(0) 就可以获得解。
纵观前面的两种方法。其实是换汤不换药。对于N>12时。计算速度有点慢了。可以进行一些优化。譬如运用对称性 ,将遍历次数减少一半。
其实该函数已经优化了很多。如果当时存放皇后的数据结构采用二维数组。IsSafe()函数将更复杂。
分析三
上述函数中,我们查看下消耗时间多的原因。由于是查所有解法,遍历次数肯定是需要 n的n次方。但是每一次中都需要判断IsSafe()函数。虽然已经将IsSafe()函数优化(只判断了已放置的皇后。)。真的有必要需要每一次都判断么?当在row行col列放置一皇后后。再row+1 行 的col-1 ,col ,col+1 都不能再放。 row+2 行的 col-2,col ,col+2 都不能再放,以此类推。
而这些不能再放的位置如果能预处理。那么IsSafe()函数将完全没必要存在。每行的可放位置都已经提前知道了。
牛人上场: N皇后的位运算版 参考来源 http://www.matrix67.com/blog/archives/266
c#版代码
private static int upperlim = (1 << 8) - 1;// 1111 1111
private static int sum = 0;
public static void Test(int row, int ld, int rd)
{
if (row != upperlim)
{
int pos = upperlim & ~(row | ld | rd);
while (pos != 0 )
{
int p = pos & -pos;//取最近一个可放位置。
pos = pos - p;//还剩下的可放位置。
Test(row + p, (ld + p) << 1, (rd + p) >> 1);
}
}
else
{
sum++;
}
}
说实话,位运算版本的可读性真的不高。不过对于N皇后问题 能找到如此好的“数据结构” 来处理该问题。真是太牛了。
其他参考:http://www.cnblogs.com/jillzhang/archive/2007/10/21/922830.html