最近在学C语言,就用C实现了一遍八皇后问题。
用回溯算法实现的(我学算法学的不系统,不确定正统的回溯算法是不是像我这样实现的)。
写程序用了一个小时,调用了一个小时。整理,重命名变量,写注释又用了一个小时。
很多人用递归实现的,在判断皇后是否冲突时需要每次执行一个循环,影响效率。
我引入了三个一维数组来分别标记指定列,左斜线,右斜线是否放置了皇后。在判断皇后是否冲突时只需要利用下标检查这三个数组就行了。
左斜线,右斜线:我自己引入的概念。
左斜线:棋盘的左上第一条斜线坐标为0,一直到右下角一共有2*n-1条左斜线。
右斜线:左下第一条斜线坐标为0,一直到右上一共有2*n-1条右斜线。
#include <stdio.h>
#include <stdlib.h>
void solveNQueens();
/*
* 获取棋盘中指定位置的右斜线索引
* 为方便回溯,需要记录斜线上是否放置了一个皇后
* 对一个指定的n,一共有(n-1)*2 + 1条右斜线
* 假设左下的斜线坐标为0,其相邻的斜线坐标1,依次加1到最右上的斜线坐标为(n-1)*2
*/
int getRigthDiagonalIndex(int row, int column);
/*
* 获取棋盘中指定位置的左斜线索引
*/
int getLeftDiagonalIndex(int row, int column);
int resultCount;//结果数
//打印结果
void printResult();
//放置一个皇后
void addAQueen(int row, int column);
//移除一个皇后
void removeAQueen(int row, int column);
//找到一行中下一个可以放皇后的位置
int findNextValidColumn(int row, int currentColumn);
int n;
int * result;//暂存一个结果
char * columnFlag;//标记:列是否放置了棋盘
char * rightDiagonalFlag;//标记:右斜线是否放置了皇后
char * leftDiagonalFlag;//标记:左斜线是否放置了皇后
int main(void)
{
printf("请输入一个整数\n");
scanf("%d", &n);
resultCount = 0;
solveNQueens();
return 0;
}
//N皇后问题
void solveNQueens()
{
int diagonalCount = n * 2 - 1;//斜线个数
result = (int*) malloc(n * sizeof(int));
columnFlag = (char*) malloc(n * sizeof(char));
rightDiagonalFlag = (char*) malloc(diagonalCount * sizeof(char));
leftDiagonalFlag = (char*) malloc(diagonalCount * sizeof(char));
int row,column,rightDiagonal,leftDiagonal,i;
//init
for ( i = 0; i < n; i++)
{
columnFlag[i] = '0';
}
for ( i = 0; i < diagonalCount; i++)
{
rightDiagonalFlag[i] = '0';
leftDiagonalFlag[i] = '0';
}
/*
* 回溯算法
*/
row = 0;
result[0] = -1;
while ( row >= 0 )
{
if ( result[row] >= 0 ) //如果当前行原来放置了一个皇后,先移除
{
removeAQueen(row, result[row]);
}
//找到下一个可以放置皇后的位置
column = findNextValidColumn(row, result[row]);
if ( column == -1 ) //该行找不到可以放皇后的位置了
{
--row;//返回上一行
}
else
{
result[row] = column;//记录皇后位置
addAQueen(row, column);//标记该皇后所在的列和斜线
++row;//到下一行
if ( row == n )//找到一个解
{
printResult(result, n);
--row;//退回到上一行
}
else//初始化下一行
{
result[row] = -1;
}
}
}
}
void addAQueen(int row, int column)
{
int rightDiagonal = getRigthDiagonalIndex(row, column);
int leftDiagonal = getLeftDiagonalIndex(row, column);
columnFlag[column] = '1';
rightDiagonalFlag[rightDiagonal] = '1';
leftDiagonalFlag[leftDiagonal] = '1';
}
int findNextValidColumn(int row, int currentColumn)
{
int column;
int rightDiagonal;
int leftDiagonal;
for( column = currentColumn + 1; column < n; column++)
{
rightDiagonal = getRigthDiagonalIndex(row, column);
leftDiagonal = getLeftDiagonalIndex(row, column);
if ( columnFlag[column] == '0' && rightDiagonalFlag[rightDiagonal] == '0'
&& leftDiagonalFlag[leftDiagonal] == '0' )
{
return column;
}
}
return -1;
}
void removeAQueen(int row, int column)
{
columnFlag[column] = '0';
int rightDiagonal = getRigthDiagonalIndex(row, column);
rightDiagonalFlag[rightDiagonal] = '0';
int leftDiagonal = getLeftDiagonalIndex(row, column);
leftDiagonalFlag[leftDiagonal] = '0';
}
int getRigthDiagonalIndex(int row, int column)
{
return (n - 1) + column - row;
}
int getLeftDiagonalIndex(int row, int column)
{
return row + column;
}
void printResult()
{
printf("第%d个解:\n", ++resultCount);
int i,j,k;
for (i = 0; i < n; i++)
{
for (j = 0; j < *(result + i); j++)
{
printf("%c ", '*');
}
printf("%c ", 'Q');
for (k = *(result + i) + 1; k < n; k++)
{
printf("%c ", '*');
}
printf("%c",'\n');
}
printf("%c",'\n');
}