分享
 
 
 

用回溯法解决皇后问题的实现思考

王朝other·作者佚名  2006-01-10
窄屏简体版  字體: |||超大  

参考资料:

算法设计与分析,宋文 吴晟 杜亚军 编著,重庆大学出版社

晨星大哥写的用递归解决的皇后问题:http://dev.hackbase.com/hackbase33/how522349.htm

回溯法是一种满足某约束条件的穷举式搜索技术,适应于解决一些组合数相当大的问题,是算法设计的重要方法之一。对于那些涉及到寻找一组解的问题或者求满足某约束条件的最优解答问题,可以用回溯法来求解。

对于考虑用回溯法求解的问题,其解必须能表示成一个n元组(x1, x2, ... , xn),其中xi是取自某个有穷集Si,|Si| = mi。解是使判定函数P取极值或满足P的问题。

一般地,不断用修改过的判定函数Pi(x1, x2, ..., xi)去测试正在构造中的n元组的部分向量(x1, x2, ..., xi),看其是否可能导致最优解。如果判定(x1, x2, ..., xi)不可能导致最优解,那么就不再测试可能要测试的mi+1,mi+2,..., mn个向量。

对于用回溯法求解的许多问题,它们要求所有的解满足一组综合的约束条件。这些约束条件可以分为两类:显示约束和隐式约束。显示约束条件可以与所求的问题的输入有关,也可以无关;而隐式约束条件则规定输入的解空间中那些实际上满足判定函数P的元组。

下面的程序实现了8皇后问题。

#include <stdio.h>

#define NUMBERQUEEN 8

int place(int x[], int k)

{

int i = 0;

while (i < k)//将显示约束条件定为i < K

{

//以下的判定条件就是隐式约束条件了

if (x[i] == x[k])//同一列上有两个皇后

{

return 0;

}

if (abs(x[i] - x[k]) == abs(i - k)))/*在同一斜对角线上有两个皇后*/

{

return 0;

}

i++;

}

return 1;

}

void NQEENS(int n)

{

int x[NUMBERQUEEN], k;

x[0] = -1;//列

k = 0; //行

while (k > -1)

{

x[k] = x[k] + 1;

while ((x[k] < n) && (place(x, k) == 0))

{

x[k] += 1;

}

if (x[k] < n)

{

if (k == n - 1)//到达棋盘的最后一行

{

int i = 0;

int count = 0;

for (i = 0; i < n; i++)

{

count++;

printf("(%d,%d)\t", i, x[i]);

if (count % 8 == 0)//很多输出时则暂停显示

{

printf("\n");

getch();

}

}

printf("\n\n");

}

else

{

k++; //下一行

x[k] = -1; //第一列开始找位置放皇后

}

}

else //退回到上一行

{

k--;

}

}

return;

}

void main(void)

{

NQEENS(NUMBERQUEEN);

getch();

return;

}

然而,对于皇后问题能否用解决呢?为了实现堆栈解决问题的想法,在网上搜索到了星辰大哥的一个递归算法。他的程序如下:

#include <stdio.h>

#include <conio.h>

#include <math.h>

#define QUEENS 8

//!记录解的序号的全局变量。

int iCount = 0;

//!记录皇后在各列上的放置位置的全局数组。

int Site[QUEENS];

//!递归求解的函数。

void Queen(int n);

//!输出一个解。

void Output();

//!判断第n个皇后放上去之后,是否有冲突。

int IsValid(int n);

/*----------------------------Main:主函数。----------------------------*/

void main()

{

//!从第0列开始递归试探。

Queen(0);

//!按任意键返回。

getch();

}

/*-----------------Queen:递归放置第n个皇后,程序的核心!----------------*/

void Queen(int n)

{

int i;

//!参数n从0开始,等于8时便试出了一个解,将它输出并回溯。

if(n == QUEENS)

{

Output();

return;

}

//!n还没到8,在第n列的各个行上依次试探。

for(i = 1 ; i <= QUEENS ; i++)

{

//!在该列的第i行上放置皇后。

Site[n] = i;

//!如果放置没有冲突,就开始下一列的试探。

if(IsValid(n))

Queen(n + 1);

}

}

/*------IsValid:判断第n个皇后放上去之后,是否合法,即是否无冲突。------*/

int IsValid(int n)

{

int i;

//!将第n个皇后的位置依次于前面n-1个皇后的位置比较。

for(i = 0 ; i < n ; i++)

{

//!两个皇后在同一行上,返回0。

if(Site[i] == Site[n])

return 0;

//!两个皇后在同一对角线上,返回0。

if(abs(Site[i] - Site[n]) == (n - i))

return 0;

}

//!没有冲突,返回1。

return 1;

}

/*------------Output:输出一个解,即一种没有冲突的放置方案。------------*/

void Output()

{

int i;

//!输出序号。

printf("No.%-5d" , ++iCount);

//!依次输出各个列上的皇后的位置,即所在的行数。

for(i = 0 ; i < QUEENS ; i++)

printf("%d " , Site[i]);

printf("\n");

}

于是根据他的程序,思索再三,有了堆栈解决问题的自然语言描述的算法:

1.nextrow从0开始直到NUMBERQUEEN

2.nextcol从0开始直到NUMBERQUEEN

3.如果栈空或者位置合适,则将该元素入栈,并从下一行继续

4.如果位置不合适,找下一个位置即下一列

5.如果栈满,则表示找到一条合适的路径,出栈并依次赋值后打印棋盘,继续寻找下个路径

6.如果列号<棋盘总的列数,称合法退出了内层循环,则继续下一行的第一列开始寻找下一个元素

7.如果列号 = 棋盘总的列数,称无合适位置而退出了内层循环

8.得到栈顶元素的行和列,并赋给循环变量,出栈,回溯到上一行的下一列寻找合适的位置元素

于是,写出了如下的程序:

#include <stdio.h>

#include <math.h>

#include <conio.h>

#define NUMBERQUEEN 8

#define MAXSIZE 8

typedef struct

{

int col; //列

int row; //行

}matrix;

typedef struct/*定义一个栈类型*/

{

matrix array[MAXSIZE];

int top;//堆栈指针

}stack;

int initstack(stack *stacks)

{

stacks->top = -1;

return 0;

}

/*将数据data压入栈stacks*/

int push(stack *stacks, int row, int col)

{

if (stacks->top == MAXSIZE)/*判断栈是否已满*/

{

//puts("The stack is full!");

return 1;

}

stacks->top++;

stacks->array[stacks->top].row = row;

stacks->array[stacks->top].col = col;

return 0;

}

/*从栈stacks中取一个元素*/

int pop(stack *stacks)

{

if (stacks->top == -1)

{

//puts("The stack is empty!");

return 1;

}

stacks->top--;

return 0;

}

int getRow(stack *stacks)

{

int itopRow;

if (stacks->top == -1)

{

//puts("The stack is empty!");

return -1;

}

itopRow = stacks->array[stacks->top].row;

return itopRow;

}

int getCol(stack *stacks)

{

int itopCol;

if (stacks->top == -1)

{

//puts("The stack is empty!");

return -1;

}

itopCol = stacks->array[stacks->top].col;

return itopCol;

}

//判栈空否

int Empty(stack stacks)

{

if (stacks.top == -1)

{

return 1;//空

}

else

{

return 0;//不空

}

}

int Full(stack stacks)

{

if (stacks.top == MAXSIZE)/*判断栈是否已满*/

{

puts("The stack is full!");

return 1;

}

return 0;

}

int IsValid(stack *stacks, int row, int col)

{

int colin, rowin;

//!将第n个皇后的位置依次于前面n-1个皇后的位置比较。

colin = getCol(stacks);

if (colin == col)

return 0;

//!两个皇后在同一对角线上,返回0。

if (abs(rowin - row) == abs(col - colin))

return 0;

//!没有冲突,返回1。

return 1;

}

void main(void)

{

int chessboard[NUMBERQUEEN][NUMBERQUEEN];

int nextrow = 0, nextcol = 0, colin = 0, rowin = 0;

stack s;

//棋盘初始化

for (nextrow = 0; nextrow < NUMBERQUEEN; nextrow++)

{

for (nextcol = 0; nextcol < NUMBERQUEEN; nextcol++)

{

chessboard[nextrow][nextcol] = 0;

printf("%5d", chessboard[nextrow][nextcol]);

}

printf("\n");

}

printf("\n");

initstack(&s);

//试探放皇后

nextrow = 0;

nextcol = 0;

while (nextrow < NUMBERQUEEN)

{

while (nextcol < NUMBERQUEEN)

{

int rIsValid;

if (Empty(s) == 1 || (rIsValid = IsValid(&s, nextrow, nextcol)) == 1)//栈空或位置合适则进栈

{ //并且转到下一行

push(&s, nextrow, nextcol);

printf("[%d][%d]\n", nextrow, nextcol);

break;

}

if (rIsValid == 0) //不合,则测试下一列

{

nextcol++;

}

}

if (Full(s) == 1)

{

int iRow, iCol;

//此处为占位代码,添加输出栈内容代码

while (!Empty(s))

{

//出栈

iRow = getRow(&s);

iCol = getCol(&s);

chessboard[iRow][iCol] = 1;

pop(&s);

}

//打印

for (rowin = 0; rowin < NUMBERQUEEN; rowin++)

{

for (colin = 0; colin < NUMBERQUEEN; colin++)

{

printf("%5d", chessboard[rowin][colin]);

}

printf("\n");

}

//修改循环变量

nextrow = iRow;

nextcol = iCol + 1;

continue;

}

if (nextcol < NUMBERQUEEN)//找到了合适的位置,则从下一行的第一列开始下个皇后的查找

{

nextrow++;

nextcol = 0;

continue;

}

if (nextcol == NUMBERQUEEN)//没有合适的位置,从上一行合法位置的下一列开始寻找新的皇后合法位置

{

nextrow = getRow(&s);//得到栈顶元素的行

nextcol = getCol(&s);//得到栈顶元素的列

pop(&s); //将不合适的栈顶元素弹出

nextcol++;//nextrow取为当前行,不修改

continue;

}

}

getch();

return;

}

这个程序包含了堆栈的一些基本操作,比较长。但可以归结为无法实现预期设想!究其原因,是因为第一行第一列元素进栈后,第二行的元素找到第三列,此时,元素chessboard[2][3]即为栈顶元素,当下一个元素再次请求进栈时,它只和栈顶元素比较位置的合法性,而和栈中其他元素无法比较位置的合法性!

期待另一种思路的出现!

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有