搜索算法小结
例题
指数型枚举
输入样例:
3
输出样例:
3
2
2 3
1
1 3
1 2
1 2 3
题解:
#include
using namespace std;
const int N = 20;
bool st[N];
int m; // 要输入的整数
void dfs(int n) {
if(n > m) { // 选完了
for(int i = 1; i <= m;i++) {
if(st[i])
cout << i << ' ';
}
cout << endl;
return;
}
// 不选这个数
dfs(n + 1);
// 选这个数
st[n] = true;
dfs(n + 1);
st[n] = false;
}
int main() {
cin >> m;
dfs(1);
return 0;
}
排列型枚举
输入样例:
3
输出样例:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
题解:
#include
using namespace std;
const int N = 10;
bool st[N];
int re[N];
int u; // 要输入的数的个数
void dfs(int n) { // n 表示第几个位置
if(n > u) { // u个位置占满了
for(int i = 1;i <= u;i++)
cout << re[i] << " ";
cout << endl;
}
for(int i = 1;i <= u;i++) {
if(!st[i]) {
st[i] = true; // 表示i已经被选中
re[n] = i;
dfs(n+1);
st[i] = false;
}
}
}
int main() {
cin >> u;
dfs(1);
return 0;
}
递归实现组合型枚举
输入样例:
5 3
输出样例:
1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5
题解:
#include
#include
using namespace std;
int n,m; // 从 n个数 中选 m个数 出来
int way[30];
void dfs(int u,int start){ // u表示正在选第u个数,已经选了u-1个数字 , start表示从数字几开始选
// 剪枝,提高搜索效率
if(u+n-startm){ // 已经找到m位数字可以输出
for(int i=1;i<=m;i++){
cout<>n>>m;
dfs(1,1); // 从第一个位置开始,从数字1开始选
return 0;
}
粘木棍
问题描述
有N根木棍,需要将其粘贴成M个长木棍,使得最长的和最短的的差距最小。
输入格式
第一行两个整数N,M。
一行N个整数,表示木棍的长度。
输出格式
一行一个整数,表示最小的差距
思路:n根木棍粘贴成m根木棍,那么需要n - m次合并,使用搜索,递归不同的合并情况
递归边界--> 最后只剩下m根木棍,合并完成(一种情况),计算最大值-最小值进行比较
题解:
#include
#include
using namespace std;
const int N = 10;
int a[N];
int n, m;
int res = INT_MAX;
// 还剩下n根木棍
void dfs(int k) {
// 还剩下m根木棍,合并完成
if (k == m) {
int pm = INT_MAX, pa = 0;
for (int i = 0; i < m; i++) {
if (pm > a[i])
pm = a[i];
if (pa < a[i])
pa = a[i];
}
res = min(res, pa - pm);
return;
}
for (int i = 0; i < k - 1; i++)
for (int j = i + 1; j < k; j++) {
a[i] += a[j]; // 合并i和j两根木棍到第i根
swap(a[j], a[k - 1]); // 第j根没用了和最后一根交换,数量-1
dfs(k - 1); // 合并减少一根木棍
// 恢复状态
swap(a[j], a[k - 1]);
a[i] -= a[j];
}
}
int main() {
cin >> n >> m;
for (int i = 0; i < n; i++)
cin >> a[i];
dfs(n);
cout << res << endl;
return 0;
}
24点游戏
思路:直接递归暴力搜索,搜索两个数的组合,每两个数的组合有四种运算符号
可以看成一次运算就是把两个数合并成一个数,那么减少的没用的数的值修改为0,作为一个数是否有效的判断条件。
题解:
#include
using namespace std;
int ans;
void dfs(int q[], int n) {
if (n == 4) {
for (int i = 0; i < 4; i++) {
if (q[i] && q[i] <= 24) {
ans = max(ans, q[i]);
break;
}
}
return;
}
// 四个数中选两个数的组合
for (int i = 0; i < 4; i++)
if (q[i] != 0)
for (int j = 0; j < 4; j++) {
if (i != j && q[j] != 0) {
int l = q[i], r = q[j];
q[i] = 0;
q[j] = l + r;
dfs(q, n + 1);
q[j] = l - r;
dfs(q, n + 1);
q[j] = l * r;
dfs(q, n + 1);
if (l % r == 0) {
q[j] = l / r;
dfs(q, n + 1);
}
// 恢复状态
q[i] = l;
q[j] = r;
}
}
}
int main() {
int n;
cin >> n;
while (n--) {
int q[4]; // 存储4个数字
ans = INT_MIN;
for (int i = 0; i < 4; i++)
cin >> q[i];
dfs(q, 1); // 第1次运算
cout << ans << endl;
}
return 0;
}
木棒
思路:
思路:这个题就是要把小棒一个个进行比较,看能不能恢复原棒,但是效率太低
1、首先是假设原木棒长度,最大的小棒 < 原木棒 < 所有小棒的长度总和,当遍历的原木棒长度 > 长度总和的一半时,原木棒的长度一定是总长sum
剪枝:1、过程中确定某根小棒是否成功被接收,在遍历某个小木棒时,对其后的木棒进行递归搜索。若当前木棒不可用,则和这根木棒相同长度的木棒也不可用,过滤掉长度相同的木棒
特殊情况:2、当拼接一根木棒的开头位置不成功,则该长度不符合;当拼接一根木棒的最后一个小木棒位置时,长度也不符合
3、当原木棒长度遍历超过总和sum 的一半时,说明原木棒的长度一定是sum,因为已经不够凑出两根原木棒了,这种情况下只能是1根。
题解
#include
#include
#include
using namespace std;
const int N = 80;
int s[N];
bool st[N];
int sum = 0, cnt, Len;
int n; // 段数
// cab当前第几段、 len当前段的长度 start开始的位置
bool dfs(int cab, int len, int start) {
if (cab > cnt)
return true; // 拼完了所有根数的原木棒
if (len == Len)
return dfs(cab + 1, 0, 0); // 拼完了一根原木棒
// 正在拼一根木棒,从start位置开始
for (int i = start; i < n; i++) {
if (st[i])
continue;
if (len + s[i] <= Len) {
st[i] = true;
if (dfs(cab, len + s[i], start + 1))
return true;
st[i] = false;
if (len == 0 || len + s[i] == Len)
return false;
while (s[i] == s[i + 1])
i++;
}
}
return false;
}
int main() {
while (cin >> n && n) {
sum = 0;
for (int i = 0; i < n; i++) {
cin >> s[i];
sum += s[i];
}
sort(s, s + n);
reverse(s, s + n); // 从大到小排序
// 遍历长度i
for (Len = s[0]; Len <= sum / 2; Len++) {
if (sum % Len)
continue;
cnt = sum / Len; // 原木棒数量
memset(st, 0, sizeof st);
if (dfs(1, 0, 0)) {
cout << Len << endl;
break;
}
}
if(Len > sum / 2) cout << sum << endl;
}
return 0;
}