八皇后问题三种实现
八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。 高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。计算机发明后,有多种计算机语言可以解决此问题。(摘自百度)
这里用回溯法和递归实现了3种。
1 C++ 回溯
2 C 回溯
3 C 递归实现
92组解
摆放棋子的规则是:
行从上向下,列从左到右试探。
C++回溯解法
#include <deque>
using namespace std;
static int const N = 8;
//
// 下棋位置
//
struct ChessPoint
{
ChessPoint(){}
ChessPoint(int x,int y)
{
this->x = x;
this->y = y;
}
ChessPoint & operator = (ChessPoint & cp)
{
this->x = cp.x;
this->y = cp.y;
return *this;
}
//横坐标,列
int x;
//纵坐标,行
int y;
};
static bool CanPlace(int x,int y,deque<ChessPoint> &vec)
{
deque<ChessPoint>::iterator it = vec.begin();
for(;it!=vec.end();it++)
{
if( it->x == x ||
it->y == y ||
it->x-x == it->y-y ||
x-it->x == it->y-y )
{
return false;
}
}
return true;
}
//
// passPath - 经过的路径
// lineNum - 现在检查的行
//
bool GetFreePoint(deque<ChessPoint> &result,ChessPoint &retCP)
{
ChessPoint cp = *result.rbegin();
int x = 0;
int y = 0;
x = cp.x+1;
y = cp.y;
result.pop_back();
//
// 这一行继续查
//
for(;;)
{
if(x>=N)
{
if(y<0)
{
break;
}
cp = *result.rbegin();
//
//试探下一列;行不变
//
x = cp.x+1;
y = cp.y;
if(x>=N)
{
//
// 此行已经试探完毕,回到上一行
//
result.pop_back();
continue;
}
result.pop_back();
}
if(CanPlace(x,y,result))
{
retCP.x=x;
retCP.y=y;
return true;
}
else
{
x+=1;
}
}
return false;
}
void queen()
{
//
// x -> (0 , N-1) , x 是列
// y -> (0 , N-1) ,y 是行
//
int x = 0;
int y = 0;
//
// 结果存放在一个vector中,有 N 个元素,每行一个
//
deque<ChessPoint> resultVec;
while(1)
{
//
// 出口
//
if(resultVec.size()==N)
{
break;
}
//
// 判断(x,y)此点是否可以
//
if(CanPlace(x,y,resultVec))
{
//
// 存入结果
//
ChessPoint pp(x,y);
resultVec.push_back(pp);
//
// 行加一
//
y+=1;
//
// 列归零
//
x=0;
}
else
{
if(x < N)
{
//
// (x,y) 此点不能匹配,移动到下一列
//
x+=1;
//
// 没有列可以选择了
//
if(x>=N)
{
//
// 没有找到匹配的列,则回溯到上一行,找到一个没有经过的坐标
//
ChessPoint po;
if(GetFreePoint(resultVec,po))
{
x = po.x;
y = po.y;
}
else
{
break;
}
}
}
else
{
//
// 没有找到匹配的列,则回溯到上一行,找到一个没有经过的坐标
//
ChessPoint po;
if(GetFreePoint(resultVec,po))
{
x = po.x;
y = po.y;
}
else
{
break;
}
}
}
}
}
C回溯解法
<span style="font-size:12px;">//
// 使用回溯法解皇后问题
// 当下一行找不到合适的位置,则返回到上一行,试探下一个位置,知道找到合适的位置;
// 当找到一个解以后,放弃这个位置,回溯找下一个合适的位置,直到找不到合适的位子,
// 就将所有解找到了。
//
//
// 要放8个皇后
//
static const int N = 8;
//
// 已经放置的皇后个数
//
static int count = 0;
//
// 解的个数
//
static int solution = 0;
//
// 下棋位置
//
struct ChessPoint
{
ChessPoint(){}
ChessPoint(int x,int y)
{
this->x = x;
this->y = y;
}
ChessPoint & operator = (ChessPoint & cp)
{
this->x = cp.x;
this->y = cp.y;
return *this;
}
//横坐标,列
int x;
//纵坐标,行
int y;
};
//
// canPlace:是否可以放置皇后呢?
// line - 行,col - 列
//
static bool CanPlace(int x,int y,unsigned char* vec)
{
for(int i=0;i<count;i++)
{
if( i == y ||
vec[i] == x ||
i-y == vec[i]-x ||
y-i == vec[i]-x )
{
return false;
}
}
return true;
}
//
// passPath - 经过的路径
// lineNum - 现在检查的行
//
static bool GetFreePoint1(unsigned char* result,ChessPoint &retCP)
{
int x = 0;
int y = 0;
x = result[count-1]+1;
y = count-1;
count-=1;
//
// 这一行继续查
//
for(;;)
{
if(x>=N)
{
if(y<0)
{
break;
}
x = result[count-1]+1;
y = count-1;
if(x>=N)
{
count-=1;
continue;
}
count-=1;
}
if(CanPlace(x,y,result))
{
retCP.x=x;
retCP.y=y;
return true;
}
else
{
x+=1;
}
}
return false;
}
//
// printResult:打印结果
// resultVec - 结果数组
//
void printResult(unsigned char *resultVec)
{
printf("-------%d皇后的解 第%d组---------\n",N,++solution);
for(int i=0;i<N;i++)
{
for(int j=0;j<N;j++)
{
if(resultVec[i]==j)
{
printf("1 ");
}
else
{
printf("0 ");
}
}
printf("\n");
}
}
//优化实现
void queen1()
{
//
// x -> (0 , N-1) , x 是列
// y -> (0 , N-1) ,y 是行
//
int x = 0;
int y = 0;
//
// 结果存放在数组中,数组的索引代表行,值代表对应的列
//
unsigned char resultVec[N];
while(1)
{
//
// 出口
//
if(count == N)
{
printResult(resultVec);
//
// 继续找下一个解
//
ChessPoint po;
if(GetFreePoint1(resultVec,po))
{
//
// 个数减一,并且回溯
//
x = po.x;
y = po.y;
}
else
{
//
//再没有解了
//
break;
}
}
//
// 判断(x,y)此点是否可以
//
if(CanPlace(x,y,resultVec))
{
//
// 存入结果
//
resultVec[y]=x;
count+=1;
//
// 行加一
//
y+=1;
//
// 列归零,从第一列试
//
x=0;
}
else
{
if(x < N)
{
//
// (x,y) 此点不能匹配,移动到下一列
//
x+=1;
//
// 没有列可以选择了
//
if(x>=N)
{
//
// 没有找到匹配的列,则回溯到上一行,找到一个没有经过的坐标
//
ChessPoint po;
if(GetFreePoint1(resultVec,po))
{
x = po.x;
y = po.y;
}
else
{
//
// 找不到合适的坐标了
//
break;
}
}
}
else
{
//
// 没有找到匹配的列,则回溯到上一行,找到一个没有经过的坐标
//
ChessPoint po;
if(GetFreePoint1(resultVec,po))
{
x = po.x;
y = po.y;
}
else
{
//
// 找不到合适的坐标了
//
break;
}
}
}
}
}
</span>
C 递归解法
<span style="font-size:10px;">//
// 这是教课书上的解法,递归解决。
// queen3 以每行作为一个单位
//
//
// 解的个数,最后8皇后得到92组解
//
static int solution = 0;
//
// 放几个皇后?
//
static const int N=8;
//
// 已经放置的个数,如果为8个,则找到一组解
//
static int count = 0;
//
// 这里存放一组解
//
static int result[N];
//
// canPlace:是否可以放置皇后呢?
// line - 行,col - 列
//
static bool canPlace(int line,int col)
{
for(int i=0;i<count;++i)
{
if( i == line ||
col == result[i] ||
i-line==col-result[i]||
line-i == col-result[i])
{
return false;
}
}
return true;
}
void printResult()
{
for(int i=0;i<N;++i)
{
for(int j=0;j<N;++j)
{
if(j == result[i])
{
printf("1 ");
}
else
{
printf("0 ");
}
}
printf("\n");
}
printf("\n");
}
//
// queen3
// line - 从哪行开始?
//
bool queen3(int line)
{
bool bFound = false;
//
// 当填满的时候打印
//
if(count == N)
{
printResult();
++solution;
printf(" %d \n",solution);
//count -= 1;
}
//
// 当到了最后一行的时候,运行到这里说明已经找到了一条可行的路径;
// 而继续查找就要回退一行,然后继续下一列,因此返回false,回退一行
//
if(line==N)
{
return false;
}
//
//i 代表列
//
for(int i=0;i<N;i++)
{
if(canPlace(line,i))
{
result[line]=i;
count+=1;
if(line<N)
{
if(!queen3(line+1))
{
count-=1;
}
else
{
bFound = true;
}
}
}
}
//
// 如果没有合适的位置,则返回false,然后就会回退一行,继续试探下一个位置
//
if(!bFound)
{
return false;
}
return true;
}</span>
//主函数调用:
void queen();
void queen1();
bool queen3(int line);
int _tmain(int argc, _TCHAR* argv[])
{
queen1();
queen();
queen3(0);
}
每一种解法是一个文件,主函数是一个文件。