深度优先遍历和广度优先遍历:可参考
数独游戏:
你一定听说过“数独”游戏。
如下图所示,玩家需要根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行、每一列、每一个同色九宫内的数字均含1-9,不重复。数独的答案都是唯一的,所以,多个解也称为无解。
本图的数字据说是芬兰数学家花了3个月的时间设计出来的较难的题目。但对会使用计算机编程的你来说,恐怕易如反掌了。
本题的要求就是输入数独题目,程序输出数独的唯一解。我们保证所有已知数据的格式都是合法的,并且题目有唯一的解。
格式要求,输入9行,每行9个数字,0代表未知,其它数字为已知。
输出9行,每行9个数字表示数独的解。
输入:005300000
800000020
070010500
400005300
010070006
003200080
060500009
004000030
000009700程序应该输出:
145327698
839654127
672918543
496185372
218473956
753296481
367542819
984761235
521839764再例如,输入:
800000000
003600000
070090200
050007000
000045700
000100030
001000068
008500010
090000400程序应该输出:
812753649
943682175
675491283
154237896
369845721
287169534
521974368
438526917
796318452
解题思路:
这里利用dfs来解题。
将每一种情况进行尝试。如果找到答案就退出。具体解法看代码
下面程序只求了一种解。
java代码示例:
1 | import java.util.Scanner; |
程序运行结果:
1 | 005300000 //输入 |
子集生成之二进制法:
请编写一个方法,返回某集合的所有非空子集。
给定一个int数组A和数组的大小int n,请返回A的所有非空子集。
保证A的元素个数小于等于20,且元素互异。各子集内部从大到小排序,子集之间字典逆序排序
java解题示例:
1 | import java.util.ArrayList; |
程序执行结果:
1 | [[C, B, A], [C, B], [C, A], [C], [B, A], [B], [A]] |
部分和:
给定整数序列a1,a2,…,an,判断是否可以从中选出若干数,使它们的和恰好为k.
1
2
3
4
5
6 > 1≤n≤20
>
> -10^8≤ai≤10^8
>
> -10^8≤k≤10^8
>
样例:
输入
1
2
3
4 > n=4
> a={1,2,4,7}
> k=13
>
输出:
1
2 > Yes (13 = 2 + 4 + 7)
>
解题思路:
有两种解题方式:
- 方案1:dfs
- 方案2:二进制法,解法同上题,子集生成之二进制法。
java解题代码:
方案1代码(dfs):
1 | import java.util.ArrayList; |
程序运行结果:
1 | 4 |
方案2(二进制选择):
1 | package chapter7_递归_DFS_剪枝_回溯; |
程序运行结果:
1 | 4 |
水洼数目:
有一个大小为 NM 的园子,雨后积起了水。八连通的积水被认为是连接在一起的。请求出
园子里总共有多少水洼?(八连通指的是下图中相对 W 的的部分)*** *W* ***
限制条件
N, M ≤ 100
样例:
输入
N=10, M=12园子如下图(’W’表示积水, ‘.’表示没有积水)
1
2
3
4
5
6
7
8
9
10
11
12 > 10 12
> W........WW.
> .WWW.....WWW
> ....WW...WW.
> .........WW.
> .........W..
> ..W......W..
> .W.W.....WW.
> W.W.W.....W.
> .W.W......W.
> ..W.......W.
>
输出
1
2 > 3
>
解题思路:
通过DFS来消除一个水洼。循环执行DFS。最后统计消掉的数量。
Java代码示例:
1 | import java.util.Scanner; |
程序运行结果:
1 | 10 12 |
回溯:
- 递归调用代表开启一个分支,如果希望这个分支返回后某些数据恢复到复制开启前的状态以便重新开始,就要使用回溯技巧。
- 例如:全排列的交换法,数独,部分和,用到了回溯。
剪枝:
- 深搜时,如已明确从当前状态无论如何转移都不会存在更优解,就应该中断往下的继续搜索,这种方法称为剪枝。
- 数独里面有剪枝。
- 部分和里面有剪枝。
n皇后问题:
请设计一种算法,解决著名的n皇后问题。这里的n皇后问题指在一个n*n的棋盘上放置n个棋子,
使得每行每列和每条对角线上都只有一个棋子,求其摆放的方法数。
给定一个int n,请返回方法数,保证n小于等于15
解题思路:
1 |
java代码示例:
1 | import java.util.Scanner; |
程序运行结果:
1 | 8 //输入 |
素数环:
输入正整数n,对1-n进行排列,使得相邻两个数之和均为素数,
输出时从整数1开始,逆时针排列。同一个环应恰好输出一次。
n<=16
*如输入:6
输出:
1 4 3 2 5 6
1 6 5 2 3 4
java代码示例:
1 | import java.util.Scanner; |
程序运行结果:
1 | 6 //输入 |
困难的数:
问题描述:如果一个字符串包含两个相邻的重复子串,则称它为容易的串,其他串称为困难的串,
如:BB,ABCDACABCAB,ABCDABCD都是容易的,A,AB,ABA,D,DC,ABDAB,CBABCBA都是困难的。
输入正整数n,L,输出由前L个字符(大写英文字母)组成的,字典序第n小的困难的串。
例如,当L=3时,前7个困难的串分别为:
A,AB,ABA,ABAC,ABACA,ABACAB,ABACABA
n指定为4的话,输出ABAC
Java代码示例:
1 | import java.util.Scanner; |
程序运行结果:
1 | 5 4 //输入 |
小结:
有一类问题,有明确的递推形式,比较容易用迭代形式实现,用递归也有较为明确的层数和宽度。
例如:走楼梯、走方格、硬币表示、括号组和、子集、全排列
有一类问题,解的空间很大(往往是阶乘级别的),要在所有可能性中找到答案,只能进行试探,尝试往前走一步,走不通就再退回来,这就是dfs + 回溯 + 剪枝。
对这类问题的优化,越早剪越好,但这很难。如素数环