回溯法的基本思想:
1.在包含问题的所有解的解空间树中(解空间:不是所有正解的集合的意思,包含完成一件事的正确部分的意思),按照深度优先搜索的策略,从根结点出发深度探索解空间树。
2.当探索到某一结点时,要判断该结点是否包含问题的解
(a)若包含,就从出发继续探索下去
(b)若该结点不包含问题的解那就说明以该结点为根结点的子树一定不包含问题的最终 解因此要跳过以该结点为根的子树的系统搜索,逐层向其祖先结点回溯,这个过程叫做“剪枝”操作。
(若求所有解,要回溯到解空间树的树根,若解决问题即可,只要搜索到一个解就可结束了)
四皇后问题的回溯法
整个解空间树为一棵四叉的满树。
其实,在解决四皇后问题时,并不一定要真的构建出这样的一棵解空间树(像堆排序的堆一样,只是一种思想),完全可以通过一个递归回溯来模拟
程序算法:
#include"stdio.h"
int count=0; //记录问题解的个数
//判断棋盘中Q[i][j]的位置是否可以放置一个皇后
int iscorrect(int i,int j,int (*Q)[4])
{
int s,t;
for(s=i,t=0;t<4;t++)
if(Q[S][t]==1&&t!=j) return 0; //判断行
for(t=j,s=0;s<4;s++) //判断列
if(Q[s][t]==1&&s!=i) return 0;
for(s=i-1,t=j-1;s>=0&&t>=0;s--,t--) //判断左上方
if(Q[s][t]==1) return 0;
for(s=i+1,t=j+1;s<4&&t<4;s++,t++) //判断右下方
if(Q[s][t]==1) return 0;
for(s=i-1,t=j+1;s>=0&&t<4;s-,t++) //判断右上方
if(Q[s][t]==1) return 0;
for(s=i+1,t=j-1;s<4&&t>=0;s++,t--) //判断左下方
if(Q[s][t]==1) return 0;
return 1; //可放
}
//核心算法
void queen(int j,int (*Q)[4])
{
//参数用j,是以列为准,找每列中的行
int i,k;
if(j==4)
{
for(i=0;i<4;i++)
{
for(k=0;k<4;k++)
printf("%d ",Q[i][k]);
printf("\n");
}
printf("\n");
getche();
count++;
return;
}
for(i=0;i<4;i++)
{
if(iscorrect(i,j,Q))
{
Q[i][j]==1;
queen(j+1,Q);
Q[i][j]==0; //******找到一个解后,清零
}
}
}
main()
{
int Q[4][4];
int i,j;
for(i=0;i<4;i++)
for(j=0;j<4;j++)
Q[i][j]=0;
queen(0,Q);
printf("The number of answers of FOUR_QUEEN are %d",count);
getche();
}
算法分析:
用一个二维数组Q[4][4]存放棋盘。Q[i][j]=0表示该位置不放置皇后,Q[i][j]=1表示该位置放置皇后。
得到一个解后,程序需要返回上一层,所以queen()用指向二维数组每一行的指针(*Q)[4]。