1、算法设计与分析,计算机科学与技术学院,陈 博,问题的提出,问题的求解思路,思维的发散与拓展,课后练习,八皇后问题是数学家高斯(Gauss)于1850年提出的趣题:,在国际象棋的88方格的棋盘上如何放置8 个皇后,使得这8个皇后不能相互攻击,即任意 两个皇后不允许处在同一横排, 同一纵列, 也 不允许处在同一与棋盘边框成45角的斜线上。,问题的提出,问题的求解思路,穷举法,优化穷举法,回溯法,四皇后,八皇后,N皇后,穷举法:,穷举法又称列举法,其基本思想是逐一列举 问题所涉及的所有情况,并根据问题提出的条件 检验哪些是问题的解,哪些应予排除。,应用穷举法设计求解,通常分以下几个步骤:,(1)根据
2、问题的具体情况确定穷举量; (2)根据指定范围设置穷举循环; (3)根据问题的具体要求确定筛选的约束条件; (4)设计穷举程序并运行调试,对运行的结果进行分析与讨论。,从特殊到一般的思维模式:,将问题的规模缩小到4皇后上,以便于实现 :,#include #include using namespace std; void FourQueens() for (int i = 1; i != 5; +i)for (int j = 1; j != 5; +j)for (int k = 1; k != 5; +k)for (int r = 1; r != 5; +r)if (i != j ,当问题所
3、涉及数量非常大时,穷举的工作量 也就相应较大,程序运行时间也就相应较长。为 此,应用穷举求解时,应根据问题的具体情况分 析归纳,寻找简化规律,精简穷举循环,优化穷 举策略。,优化穷举法:,高斯8皇后问题的一个解用一个8位数表示, 8位数解的第K个数字为j,表示棋盘上的第k行 的第j格放置一个皇后。,任意两个皇后不允许处在同一横排,同一纵 列,要求8位数中数字18各出现一次,不能重复。 因而解的范围区间应为12345678,87654321。 注意到数字18的任意一个排列的数字和为9的倍 数,因而穷举a循环的循环步长可优化为9。,为了判别数字18在8位数a各出现一次,设 置f数组,f(x)统计a
4、中数字x的个数。若f(1) f(8)均等于1,即数字18在a中各出现一次。 否则,返回测试下一个8位数a。,任意两个皇后不允许处在同一与棋盘边框成 45角的斜线上,设置g数组,若a的第k个数字为 x,则g(k)=x。要求解的8位数的第j个数字与 第k个数字的绝对值不等于j k(设置j k)。若 出现:,| g(j)- g(k)| = j - k,表明j与k出现同处在与棋盘边框成45角的斜 线上,返回测试下一个8位数a。,#include #include void main() int s,k,i,j,t,x,f9,g9;long a,y;s=0;printf(“高斯8皇后问题的解为:n“);
5、for(a=12345678;a0)x=y%10;fx=fx+1;y=y/10;k+;,gk=x; /分离各数字,用f数组统计for(t=0,i=1;i=8;i+)if(fi!=1) t=1;if(t=1) continue; /数字18出现不为1次,返回for(k=1;k=7;k+)for(j=k+1;j=8;j+)if(fabs(gj-gk)=j-k) t=1;if(t=1) continue; /同处在45度角的斜线上,返回s+; /输出8皇后问题的解printf(“%ld “,a);if(s%6=0) printf(“n“);printf(“n s=%dn“,s); ,回溯法:,回溯法
6、是一种试探求解的方法:通过对问 题的归纳分析,找出求解问题的一个线索,沿 着这一线索往前试探,若试探成功,即得到解; 若试探失败,就逐步往回退,换其他路线再往 前试探。因此,回溯法可以形象地概括为 “向 前走,碰壁回头”。,回溯法的基本做法是试探搜索,是一种组织 得井井有条的、能避免一些不必要搜索的穷举式 搜索法。这种方法适用于一些组合数比较大的问 题。回溯算法在问题的解空间树中,从根结点出 发搜索解空间树,搜索至解空间树的任意一点, 先判断该结点是否包含问题的解;如果肯定不包 含,则跳过对该结点为根的子树的搜索,逐层向 其父结点回溯;否则,进入该子树,继续搜索。,与穷举法相比,回溯法的“聪明
7、”之处在于能 适时“回头”,若再往前走不可能得到解,就回溯, 退一步另找线路,这样可省去大量的无效操作。 因此,回溯和穷举相比,回溯更适宜于量比较大, 候选解比较多的问题。,思维的发散与拓展,斜率法求解八皇后:,#include /a存放皇后所在的列数,比如a1=1表示第一个皇后在第一列上 /b的状态表示该列是否被占,比如b1=1表示第1列被占,b1=0表示第1列未被占 /ci+j的状态表示某斜率为1的对角线上是否被占,1表示被占 /di-j+n的状态表示某斜率为-1的对角线上是否被占,1表示被占 int a20,b20,d40,c40,n,t,i,j,k; void output() /输出
8、解决方案t=t+1;printf(“第%2d种方案: “,t);for(k=1;k=n;k+) printf(“%2d “,ak);,printf(“n“); void tackle(int i) int j;for(j=1;j=n;j+) /列数的移动if( (bj=0) /表示某斜率为-1的对角线被占if(in), /小于8则递归tackle(i+1);else /处理完则输出output();bj=0; /重新置标记位为0ci+j=0;di-j+n=0; ,int main() int i; key:printf(“请输入8处理8皇后问题n“);scanf(“%d“,tackle(1); /从第一个皇后开始放,第i个皇后在第i行上寻找位置elsegoto key;return 0; ,课后练习,日本数学家桥本吉彦教授于1993年10月在 我国山东举行的中日美三国数学教育研讨会上 向与会者提出以下填数趣题:把1,2,9这9 个数字填入下式的9个方格中(数字不得重复), 使下面的分数等式成立。,这一分数式填数趣题究竟共有多少个解答? 试求出所有解答(等式左边两个分数交换次序只 算一个解答)。,,谢谢,再见!,