前言
概念
蛮力法(brute force):直接基于问题的描述和所涉及的概念定义的进行算法设计,简单而直接。
蛮力法应用特点
- 蛮力法所能解决的问题跨越的领域非常广泛。
- 对于一些重要的问题,运用蛮力策略可以设计出具备一定实用价值的算法,并且不用限制实例的规模。
- 当要解决的问题实例不多并且可以接受蛮力法的运算速度时,蛮力法的设计代价通常较为低廉。
- 蛮力算法可以作为衡量其它算法的准绳,服务于研究或教学。
枚举法
算法框架
- 依据问题,设定枚举范围;
- 找出约束条件,建立计算模型;
- 利用计算模型在枚举范围内搜索可能的解。
例题1
输入n个数字(在0与9之间),然后统计出这组数中相邻两个数字组成的链环数字对出现的次数。如:n=20,输入为0 1 5 9 8 7 2 2 2 3 2 7 8 7 8 7 9 6 5 9,则输出为(7,8)=2,(8,7)=3,(7,2)=1,(2,7)=1,(2,2)=2,(2,3)=1,(3,2)=1。
问题分析
可以利用一个二维数组
a
[
10
]
[
10
]
a[10][10]
a[10][10]存储出现的数字队。
首先,将二维数组初始化
a
[
i
]
[
j
]
=
0
(
0
≤
i
<
10
,
≤
j
<
10
)
a[i][j] = 0 \quad(0\leq i<10,\leq j<10)
a[i][j]=0(0≤i<10,≤j<10)
然后,每输入一个数字对,则把对应下标的数组元素加1,如数据输入的序列为
x
x
x,则
a
[
x
k
]
[
x
k
+
1
]
=
a
[
x
k
]
[
x
k
+
1
]
+
1
a[x_k][x_{k+1}] = a[x_k][x_{k+1}]+1
a[xk][xk+1]=a[xk][xk+1]+1
如题目中的例子可以构建出如下的二维数组:
计算模型
输入序列:
x
x
x
初始化:
a
[
i
]
[
j
]
=
0
(
0
≤
i
<
10
,
≤
j
<
10
)
a[i][j] = 0 \quad(0\leq i<10,\leq j<10)
a[i][j]=0(0≤i<10,≤j<10)
a
[
x
k
]
[
x
k
+
1
]
=
a
[
x
k
]
[
x
k
+
1
]
+
1
0
≤
k
<
n
a[x_k][x_{k+1}] = a[x_k][x_{k+1}] +1 \quad 0 \leq k <n
a[xk][xk+1]=a[xk][xk+1]+10≤k<n
最后,统计完毕后,输出a
算法设计与描述
算法分析
例题2
解数字谜,如下
问题分析
一共可以采用三种方式
- 枚举测试:五位数的范围为30000-99999。时间复杂度为99999-30000 + 1 = 70000次
- 采用构造式的枚举法:A取值从3-9,B-C取值0-9。时间复杂度为71010 = 700
- 构造式的枚举法——除法:D的值从1-9,而A的取值仍然从3-9。两重循环,时间复杂度为7*9=63。
计算模型
主要针对第三种进行分析
设
A
i
∈
[
3
,
9
]
,
D
j
∈
[
1
,
9
]
A_i\in [3,9],D_j \in [1,9]
Ai∈[3,9],Dj∈[1,9]
{
D
=
D
j
∗
1
0
5
+
⋯
+
D
j
D
m
o
d
A
i
=
=
0
上
一
步
算
得
的
D
可
以
被
A
i
整
除
S
i
=
D
/
A
i
B
1
=
S
i
m
o
d
10
A
1
=
(
S
i
/
10
)
m
o
d
10
C
=
(
S
i
/
1
0
2
)
m
o
d
10
B
2
=
(
S
i
/
1
0
3
)
m
o
d
10
A
2
=
(
S
i
/
1
0
4
)
m
o
d
10
\left\{ \begin{array}{lr} D = D_j*10^5 +\dots +D_j & \\ D \;mod\; A_i == 0&上一步算得的D可以被A_i整除\\ S_i = D/A_i & \\ B_1 = S_i\; mod\; 10& \\ A_1 = (S_i/10) \; mod \;10\\ C = (S_i/10^2) \; mod \;10\\ B_2 = (S_i/10^3)\; mod\; 10& \\ A_2 = (S_i/10^4) \; mod \;10\\ \end{array} \right.
⎩⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎧D=Dj∗105+⋯+DjDmodAi==0Si=D/AiB1=Simod10A1=(Si/10)mod10C=(Si/102)mod10B2=(Si/103)mod10A2=(Si/104)mod10上一步算得的D可以被Ai整除
判断
D
D
D是否可以被
A
i
A_i
Ai整除,
A
1
A_1
A1与
A
2
A_2
A2是否相等,
B
1
B_1
B1和
B
2
B_2
B2是否相等,上述条件均成立,则找到一个解。
算法设计与描述
输出:五位数ABCAB,六位数DDDDDD
例题3
输出玫瑰矩阵,其为n*n的方阵,特征如下所示对应下标的数组元素加1
问题分析
设矩阵为 a [ n , n ] a[n,n] a[n,n]可以采用两种思路
- 用
i
i
i代表圈数(从0开始),j代表圈内下标变化量:
{ a [ j , i ] ← k k ← k + 1 j ← j + 1 左 侧 a [ n − i − 1 , j ] ← k k ← k + 1 j ← j + 1 底 侧 a [ j , n − i − 1 ] ← k k ← k + 1 j ← j − 1 右 侧 a [ i , j ] ← k k ← k + 1 j ← j − 1 顶 侧 \left\{ \begin{array}{lr} a[j,i]\gets k & k\gets k+1& j\gets j+1&左侧\\ a[n-i-1,j]\gets k & k\gets k+1& j\gets j+1&底侧\\ a[j,n-i-1]\gets k & k\gets k+1& j\gets j-1&右侧\\ a[i,j]\gets k & k\gets k+1& j\gets j-1&顶侧\\ \end{array} \right. ⎩⎪⎪⎨⎪⎪⎧a[j,i]←ka[n−i−1,j]←ka[j,n−i−1]←ka[i,j]←kk←k+1k←k+1k←k+1k←k+1j←j+1j←j+1j←j−1j←j−1左侧底侧右侧顶侧
若n为奇数,还需要最后处理,令 a [ ( n − 1 ) / 2 , ( n − 1 ) / 2 ] = n 2 a[(n-1)/2,(n-1)/2] = n^2 a[(n−1)/2,(n−1)/2]=n2 - 可以看出每半圈元素值增长规律变换一次
设增量 t = 1 t = 1 t=1,每半圈变换一次 t ← − t t \gets -t t←−t
设矩阵边长为i,每半圈的元素就是 2 ∗ i − 1 2*i-1 2∗i−1个, h c hc hc为半圈内计数变量, 0 ≤ h c < 2 ∗ i − 1 0\leq hc<2*i-1 0≤hc<2∗i−1,前1/4圈是 0 ≤ h c < i 0\leq hc<i 0≤hc<i,后1/4圈是 i ≤ h c < 2 ∗ i − 1 i\leq hc<2*i-1 i≤hc<2∗i−1。
其中 i i i从 n n n开始变换,每过半圈 i = i − 1 i = i-1 i=i−1
计算模型
算法设计与描述
算法分析
穷举查找
有一些求最优解的问题经过抽象,可以转换为组合优化问题,使用蛮力法来求解是一种简单的方法,称之为穷举查找(exhaustive search)。
例题4
旅行商问题(traveling salesman problem,TSP) 有一个旅行商由某市出发,经过所有给定的n个城市后,再回到出发的城市。除了出发的城市外,其它城市只经过一回。这样的回路可能有多个,求其中路径成本最小的回路。
问题分析
给出一个问题实例,如下
可以通过遍历a - d的全排列来实现问题求解。
计算模型
- 存储 利用图G(V,E)。边以临界矩阵形式存储,如下
- 计算 列出所有路径
a - b - c - d - a
a - b - d - c - a
a - c - b - d - a
a - c - d - b - a
a - d - b - c - a
a - d - c - b - a - 计算路径代价
算法设计与描述
算法分析
T ( n ) = ∑ i = 0 n − 1 T ( i + 1 ) + C T(n)=\sum_{i=0}^{n-1}T(i+1)+C T(n)=i=0∑n−1T(i+1)+C
例题5
背包问题。给定 n n n个重量为 w 1 , w 2 , … w n w_1, w_2,…w_n w1,w2,…wn,价值为 v 1 , v 2 , … v n v_1, v_2,…v_n v1,v2,…vn的物品和一个承重为 W W W的背包,求将这些物品中的某些装入背包中,在不超出重量 W W W的情况下,价值最高的装法。
问题分析
计算模型
算法设计与描述
图的搜索
图的两个重要的遍历算法:深度优先查找(depth-first search, DFS)和广度优先查找(breadth-frist search, BFS),是穷举查找的重要应用之一。
深度优先查找
广度优先查找
例题6
迷宫问题。如图所示,图中方格内标为0的为通路,标为1墙。(0,0)为迷宫入口,(7,7)为迷宫出口,请查出由(0,0)到(7,7)的路径。
问题分析
- 存储
- 移动
计算模型
- 存储
采用二维矩阵maze[n][n],进行存储
其中maze[x][y] = 0表示通路,maze[x][y]=1表示墙,maze[x][y]=2表示死路,maze[x][y] = 3表示已经走过。 - 行走相对路径
行走方向:fx[]={-1,1,0,0 },fy[]={0,0, -1,1}
行走过程:nextx=x+fx[i],nexty=y+fy[i]
其中,i=0, 1, 2, 3。
算法设计与描述
算法分析
- 迷宫矩阵:maze[n][n]
- 依据不同的行走方向,可知
T
(
n
,
n
)
=
T
(
n
−
1
,
n
)
+
T
(
n
+
1
,
n
)
+
T
(
n
,
n
−
1
)
+
T
(
n
,
n
+
1
)
T(n, n)=T(n-1,n)+T(n+1,n)+T(n, n-1)+T(n,n+1)
T(n,n)=T(n−1,n)+T(n+1,n)+T(n,n−1)+T(n,n+1)。然而,这公式并不准确,因为墙的布局对算法的影响很大,所以,最坏情况下,所有顶点都测试过,这时,时间复杂度为
T ( n ) = O ( ( n ∗ n − 1 ) ∗ 4 ) T(n)=O((n*n-1)*4) T(n)=O((n∗n−1)∗4) - 存储空间中包括递归时的所用的栈,所以,空间复杂度至少为 O ( 2 ∗ n 2 ) O(2*n^2) O(2∗n2)。
参考资料:张小东老师ppt