您的位置:首页 > 其它

八皇后问题

2016-10-28 11:26 127 查看
1.什么是八皇后问题?
八皇后问题是一个以国际象棋为背景的问题:如何能够在 8×8 的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上。八皇后问题可以推广为更一般的n皇后摆放问题:这时棋盘的大小变为n×n,而皇后个数也变成n。而且仅当 n = 1 或 n ≥ 4 时问题有解。
八皇后问题最早是由国际西洋棋棋手马克斯·贝瑟尔于1848年提出。之后陆续有数学家对其进行研究,其中包括高斯和康托,并且将其推广为更一般的n皇后摆放问题。八皇后问题的第一个解是在1850年由弗朗兹·诺克给出的。诺克也是首先将问题推广到更一般的n皇后摆放问题的人之一。1874年,S.冈德尔提出了一个通过行列式来求解的方法,这个方法后来又被J.W.L.格莱舍加以改进。
艾兹格·迪杰斯特拉在1972年用这个问题为例来说明他所谓结构性编程的能力。
八皇后问题出现在1990年代初期的著名电子游戏第七访客中。



2.求解思路:
问题比较显而易见的是,一共要放置8个皇后.每行放置一个,在每一行放置皇后时,初始有8个位置可以放置。但是由于前面已经放置皇后的限制,要在8个位置上寻找到可以放置的位置。直到8行均找到放置位置,问题有一个解。在某一行如果无法找到放置位置,需要回溯到上一行,在上一行已经找到位置的基础上寻找下一个可以放置的位置,继续进行这个过程。如果要找到所有可行解,在找到第一个解后,继续需找最后一行的下一个可以放置位置。直到回溯到第一行无法找到可以放置位置,即找到全部解。

3.非递归实现:
伪代码:
1.初始化一个二维数组,每个位置置0,放置皇后置1.
2.依次对每一行寻找放置皇后的位置,如果可以放置,将位置置1,继续下一行;否则,回溯到上一行的下一个位置
进行判断,并将上一个可以放置位置置0.重复此过程,直到所有行均有可放置位置.打印一组解.
3.找到一组解后,试图在最后一行下个位置寻找解,直到回溯到第一行也没有可以放置位置.即找到所有解.
struct point
{
int x;
int y;
};

int Eight[][8] = {0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0};
bool Put(int array[][8], point pt)
{
for (int i = 0; i<8; i++) // 横坐标
{
for (int j = 0; j<8; j++) // 纵坐标
{
if (array[i][j] == 1 ) // 如果当前坐标有皇后,根据此点判断传入点是否可以放置
{
if(pt.x == i || pt.y == j) // 判断是否在同一行或者同一列
return false;
// 每个点四个方向
// 左上
/*int m = i;
int n = j;
while ((--m>=0) && (--n >=0))
{
if (pt.x == m && pt.y == n)
{
return false;
}
}
// 右上
m = i;
n = j;
while ((--m>=0) && (++n) <= 7)
{
if (pt.x == m && pt.y == n)
{
return false;
}
}*/
// 左下
int m = i;
int n = j;
while ((++m <= 7) && (--n >= 0))
{
if (pt.x == m && pt.y == n)
{
return false;
}
}
// 右下
m = i;
n = j;
while (++m<=7 && ++n <= 7)
{
if (pt.x == m && pt.y == n)
{
return false;
}
}

}

}
}
return true;
}
// 打印八皇后的解
void Print()
{
for (int m = 0; m<8; m++)
{
for (int n = 0; n<8; n++)
{
printf("%d, ", Eight[m]
);
}
printf("\r\n");
}
}
int nCount = 0;
for(int i = 0; i<8; i++)
{
bool bFind = false; // 标识当前行是否找到解
int j = 0;
// 每次寻找插入位置时,判断是否当前行已经有放置皇后的位置,如果有
// 需要从下一个位置开始寻找,并将原位置置0
for (int k = 0; k<8; k++)
{
if (Eight[i][k] == 1)
{
j = k + 1;
Eight[i][k] = 0;
break;
}
}
for (j; j<8; j++)
{
point pt;
pt.x = i;
pt.y = j;
if(Put(Eight, pt)) // 如果找到了可以放置的坐标,寻找下一行
{
bFind = true;
Eight[pt.x][pt.y] = 1;
break;
}else
{
continue;
}
}
if (bFind) // 不用回溯
{
if (i == 7) // 找到一种解法
{
nCount++;
Print();
i--;//试图找到下一种解法
printf("%d\r\n", nCount);
//getchar();
}

}else // 回溯到上一层
{
i -= 2;
if(i < -1)
{
printf("over");
getchar();
}
}
}
4.递归实现
static int gEightQueen[8] = { 0 }, gCount = 0;
void print()//输出每一种情况下棋盘中皇后的摆放情况
{
for (int i = 0; i < 8; i++)
{
int inner;
for (inner = 0; inner < gEightQueen[i]; inner++)
cout << "-";
cout <<"#";
for (inner = gEightQueen[i] + 1; inner < 8; inner++)
cout << "-";
cout<<endl;
}
cout << "==========================\n";
}

/* loop表示在第几行放置, value当前放置的位置
0表示当前位置不可用,1表示当前位置可以放置*/
int check_pos_valid(int loop, int value)//检查是否存在有多个皇后在同一行/列/对角线的情况
{
int index;
int data;
for (index = 0; index < loop; index++) // 对前面每个元素的位置进行判断,看是否符合要求
{
data = gEightQueen[index];
if (value == data) // 同一列
return 0;
if ((index + data) == (loop + value)) // 左下对角线
return 0;
if ((index - data) == (loop - value)) // 右下对角线
return 0;
}
return 1;
}
void eight_queen(int index)
{
int loop;
for (loop = 0; loop < 8; loop++) // 对8个位置进行判断
{
if (check_pos_valid(index, loop))
{
gEightQueen[index] = loop;
if (7 == index)
{
gCount++, print();
gEightQueen[index] = 0; // 将当前位置置0,试图寻找下一个解
return;
}
eight_queen(index + 1);
// 没有找到,上面返回,将上一层置0,重新寻找
gEightQueen[index] = 0;
}
}
}
<
4000
br />
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  八皇后 递归 回溯