暴力枚举算法引入:
很多问题都可以“暴力解决”——不用动太多脑筋,把所有可能性都列举出来,然后一一试验。尽管这样的方法很“笨”,但却常常是行之有效的。
本篇文章包括以下内容:
简单枚举 - 枚举排列 - 子集生成
>> 简单枚举
在枚举复杂对象之前,先尝试着枚举一些相对简单的内容,如整数、字串等。
洛谷练习题:
除法 Division - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
最大乘积 Maximum Product - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
分数拆分 Fractions Again?! - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
>> 枚举排列
1 生成1 - n的排列
代码实现:
int a[3] = {0}, n = 3;
void dfs(int cur) {
if (cur == n) {
for (int i = 0; i < n; i++)
printf("%d ", a[i]);
printf("\n");
return;
}
for (int i = 1; i <= n; i++) {
int st = 1;
for (int j = 0; j < cur; j++)
if (a[j] == i)
st = 0;
if (st) {
a[cur] = i;
dfs(cur + 1);
}
}
}
输出结果:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
2 生成可重集的排列
代码实现:
int a[3] = {0}, b[3] = {1, 1, 2}, n = 3;
void dfs(int cur) {
if (cur == n) {
for (int i = 0; i < n; i++)
printf("%d ", a[i]);
printf("\n");
return;
}
for (int i = 0; i < n; i++)
if (i == 0 || b[i] != b[i - 1]) {
int c1 = 0, c2 = 0;
for (int j = 0; j < n; j++)
if (b[j] == b[i])
c1++;
for (int j = 0; j < cur; j++)
if (a[j] == b[i])
c2++;
if (c2 < c1) {
a[cur] = b[i];
dfs(cur + 1);
}
}
}
输出结果:
1 1 2
1 2 1
2 1 1
3 下一个排列
枚举所有排列的另一个方法是从字典序最小的排列开始,不停调用“求下一个排列的过程”。
C++的STL中提供了一个库函数 next_permutation。
代码实现:
#include <algorithm>
int a[3] = {1, 2, 3};
next_permutation(a, a + 3);
printf("%d %d %d", a[0], a[1], a[2]);
输出结果:
1 3 2
>> 子集生成
给定一个集合,枚举所有可能的子集。(没有重复元素)
1 构造增量法
int a[3] = {0}, n = 3;
void dfs(int cur) {
for (int i = 0; i < cur; i++)
printf("%d ", a[i]);
printf("\n");
int s = cur ? a[cur - 1] + 1 : 1;
for (int i = s; i <= n; i++) {
a[cur] = i;
dfs(cur + 1);
}
}
输出结果:
1
1 2
1 2 3
1 3
2
2 3
3
2 位向量法
代码实现:
int a[3] = {0}, n = 3;
void dfs(int cur) {
if (cur == n) {
for (int i = 0; i < n; i++)
if (a[i])
printf("%d ", i + 1);
printf("\n");
return;
}
a[cur] = 1;
dfs(cur + 1);
a[cur] = 0;
dfs(cur + 1);
}
输出结果:
1 2 3
1 2
1 3
1
2 3
2
3
3 二进制法
int n = 3;
void dfs(int cur, int s) {
for (int i = 0; i < n; i++)
if (s & (1 << i))
printf("%d ", i + 1);
printf("\n");
}
for (int i = 0; i < (1 << n); i++)
dfs(n, i);
输出结果:
1
2
1 2
3
1 3
2 3
1 2 3
洛谷练习题:
P1157 组合的输出 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
P1706 全排列问题 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
P1088 [NOIP2004 普及组] 火星人 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
P1036 [NOIP2002 普及组] 选数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
P2036 [COCI 2008/2009 #2] PERKET - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)