例题

AcWing 92. 递归实现指数型枚举

从 1∼n 这 n 个整数中随机选取任意多个,输出所有可能的选择方案。

输入格式
输入一个整数 n。

输出格式
每行输出一种方案。

同一行内的数必须升序排列,相邻两个数用恰好 1 个空格隔开。

对于没有选任何数的方案,输出空行。

本题有自定义校验器(SPJ),各行(不同方案)之间的顺序任意。

数据范围

1≤n≤15

输入样例:

3

输出样例:


3
2
2 3
1
1 3
1 2
1 2 3

思路 :

  • 实际上每个数只要考虑选(1)和不选(2)这两个状态,也就是说回溯回来不需要恢复成0,相当于每个结点有两个分叉,所以是 2n2^n2n 指数型枚举
  • dfs时,结束递归时一定要return
#include <iostream>
using namespace std;const int N = 16;int n;
int st[N];      // 0表示还没考虑 1表示选 2表示不选void dfs(int u)
{if (u > n){for (int i = 1; i <= n; i ++ )if (st[i] == 1)printf("%d ", i);puts("");return ;}st[u] = 2;dfs(u + 1);// st[u] = 0;st[u] = 1;dfs(u + 1);// st[u] = 0;
}int main()
{scanf("%d", &n);dfs(1);return 0;
}

AcWing 94. 递归实现排列型枚举

把 1∼n 这 n 个整数排成一行后随机打乱顺序,输出所有可能的次序。

输入格式
一个整数 n。

输出格式
按照从小到大的顺序输出所有方案,每行 1 个。

首先,同一行相邻两个数用一个空格隔开。

其次,对于两个不同的行,对应下标的数一一比较,字典序较小的排在前面。

数据范围

1≤n≤9

输入样例:

3

输出样例:

1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1

思路 :

  • 有n∗(n−1)∗...∗1n*(n-1)*...*1n∗(n−1)∗...∗1种排列方式,因此是排列型枚举
  • 每次递归决定当前这个盒子中放什么数字,由于不重复放,因此开一个数组记录当前是否用过这个数字,因此回溯要恢复现场
#include <iostream>
using namespace std;const int N = 10;int n;
int state[N];
bool st[N];void dfs(int u)
{if (u > n){for (int i = 1; i <= n; i ++ )printf("%d ", state[i]);puts("");return ;}for (int i = 1; i <= n; i ++ ){if (!st[i]){st[i] = true;state[u] = i;dfs(u + 1);// state[u] = 0;st[i] = false;}}
}int main()
{scanf("%d", &n);dfs(1);return 0;
}

AcWing 717. 简单斐波那契

以下数列 0 1 1 2 3 5 8 13 21 … 被称为斐波纳契数列。

这个数列从第 3 项开始,每一项都等于前两项之和。

输入一个整数 N,请你输出这个序列的前 N 项。

输入格式
一个整数 N。

输出格式
在一行中输出斐波那契数列的前 N 项,数字之间用空格隔开。

数据范围

0<N<46

输入样例:

5

输出样例:

0 1 1 2 3
// 递推
#include <iostream>
using namespace std;int main()
{int n; scanf("%d", &n);int a = 0, b = 1, c;for (int i = 0; i < n; i ++ ){printf("%d ", a);c = a + b, a = b, b = c;}
}
#include <iostream>
using namespace std;const int N = 47;int n;
int f[N];int dfs(int u)
{if (u == 1 || u == 2 || f[u]) return f[u];return f[u] = dfs(u - 1) + dfs(u - 2);
}int main()
{scanf("%d", &n);f[1] = 0, f[2] = 1;dfs(n);for (int i = 1; i <= n; i ++ ) cout << f[i] << ' ' ;return 0;
}

AcWing 95. 费解的开关

你玩过“拉灯”游戏吗?

25 盏灯排成一个 5×5 的方形。

每一个灯都有一个开关,游戏者可以改变它的状态。

每一步,游戏者可以改变某一个灯的状态。

游戏者改变一个灯的状态会产生连锁反应:和这个灯上下左右相邻的灯也要相应地改变其状态。

我们用数字 1 表示一盏开着的灯,用数字 0 表示关着的灯。

下面这种状态

10111
01101
10111
10000
11011
在改变了最左上角的灯的状态后将变成:

01111
11101
10111
10000
11011
再改变它正中间的灯后状态将变成:

01111
11001
11001
10100
11011
给定一些游戏的初始状态,编写程序判断游戏者是否可能在 6 步以内使所有的灯都变亮。

输入格式
第一行输入正整数 n,代表数据中共有 n 个待解决的游戏初始状态。

以下若干行数据分为 n 组,每组数据有 5 行,每行 5 个字符。

每组数据描述了一个游戏的初始状态。

各组数据间用一个空行分隔。

输出格式
一共输出 n 行数据,每行有一个小于等于 6 的整数,它表示对于输入数据中对应的游戏状态最少需要几步才能使所有灯变亮。

对于某一个游戏初始状态,若 6 步以内无法使所有灯变亮,则输出 −1。

数据范围

0<n≤500

输入样例:

3
00111
01011
10001
11010
1110011101
11101
11110
11111
1111101111
11111
11111
11111
11111

输出样例:

3
2
-1

思路 :

  • 目标是把所有开关全部变成1,由于当上一行的状态确定时,若上一行存在0的状态,只能由下一行的位置影响上一行的0(注意这是四连通),因此可以使用递推
  • 只要第0行开关状态确定,则所有开关的状态都可以递推出来,因此只要枚举第一行状态的所有情况,有252^525种
  • 从第0行递推出第1到第4行的所有状态,若当前行状态已确定,且存在开关是0状态的,则需要下一行的位置对开关进行切换,影响当前行开关是0的状态
  • 最后枚举最后一行,若该状态全部是1,则表示成功,更新最小步数

语法 :

  • char二维数组的输入方式
  • 第一行一共32种状态,也就是[0,31][0,31][0,31],op >> i & 1
  • 每种枚举方式中,一开始用backup备份g,最后再备份回来
  • 如果res大于6,直接赋值为-1
  • 即使g二维数组是char类型,但可以g[a][b]^=1,0变成1,1变成0
#include <iostream>
#include <cstring>
using namespace std;const int N = 6;char g[N][N], backup[N][N];
int dx[5] = {0, 1, 0, -1, 0}, dy[5] = {1, 0, -1, 0, 0};void turn(int x, int y)
{for (int i = 0; i < 5; i ++ ){int a = x + dx[i], b = y + dy[i];if (a < 0 || a > 4 || b < 0 || b > 4) continue;g[a][b] ^= 1;}
}int main()
{int _; scanf("%d", &_);while (_ -- ){for (int i = 0; i < 5; i ++ ) cin >> g[i];int res = 10;for (int op = 0; op < 32; op ++ ){memcpy(backup, g, sizeof g);int step = 0;for (int i = 0; i < 5; i ++ )if (op >> i & 1){step ++ ;turn(0, i);}for (int i = 0; i < 4; i ++ )for (int j =0 ; j < 5; j ++ )if (g[i][j] == '0'){step ++ ;turn(i + 1, j);}bool dark = false;for (int i = 0; i < 5; i ++ )if (g[4][i] == '0'){dark = true;break;}if (!dark) res = min(res, step);memcpy(g, backup, sizeof backup);}if (res > 6) res = -1;printf("%d\n", res);}
}

习题

AcWing 93. 递归实现组合型枚举

从 1∼n 这 n 个整数中随机选出 m 个,输出所有可能的选择方案。

输入格式
两个整数 n,m ,在同一行用空格隔开。

输出格式
按照从小到大的顺序输出所有方案,每行 1 个。

首先,同一行内的数升序排列,相邻两个数用一个空格隔开。

其次,对于两个不同的行,对应下标的数一一比较,字典序较小的排在前面(例如 1 3 5 7 排在 1 3 6 8 前面)。

数据范围

n>0 ,
0≤m≤n ,
n+(n−m)≤25

输入样例:

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

思考题:如果要求使用非递归方法,该怎么做呢?

思路 :

  • dfs第一个参数为当前放到第i个位置(一共m个),第二个参数为这一次开始选择的数字(比上一次的大)
  • 枚举边界为放到第m+1个位置,也就是说放完了
  • 剪枝,加上后面放的数不足m个
#include <iostream>
using namespace std;const int N = 30;int n, m;
int state[N];void dfs(int u, int start)
{if (u + n - start < m) return ;if (u == m + 1){for (int i = 1; i <= m; i ++ )cout << state[i] << ' ';cout << endl;return ;}for (int i = start; i <= n; i ++ ){state[u] = i;dfs(u + 1, i + 1);}
}int main()
{cin >> n >> m;dfs(1, 1);return 0;
}

AcWing 1209. 带分数

100 可以表示为带分数的形式:100=3+69258714
还可以表示为:100=82+3546197
注意特征:带分数中,数字 1∼9 分别出现且只出现一次(不包含 0)。

类似这样的带分数,100 有 11 种表示法。

输入格式
一个正整数。

输出格式
输出输入数字用数码 1∼9 不重复不遗漏地组成带分数表示的全部种数。

数据范围

1≤N<106

输入样例1:

100

输出样例1:

11

输入样例2:

105

输出样例2:

6

思路 :

  • 给一个数n,问有多少组a+bc=na+\frac{b}{c}=na+cb​=n,且abc三个数不重不漏地涵盖1-9这9个数字
  • 暴力枚举9个数的全排列,然后用一个长度为9的数组保存全排列的结果
  • 从全排列的结果中用两重循环暴力分解出三段,每段代表一个数
  • 验证枚举出的三个数是否满足题干条件,若满足则计数
#include <iostream>
using namespace std;int n, cnt;
bool st[10];
int state[10];int calc(int l, int r)
{int res = 0;for (int i = l; i <= r; i ++ )res = res * 10 + state[i];return res;
}void dfs(int u)
{if (u == 10){for (int i = 1; i < 10; i ++ )for (int j = i + 1; j < 9; j ++ ){int a = calc(1, i);int b = calc(i + 1, j);int c = calc(j + 1, 9);if (a * c + b == n * c) cnt ++ ;}return ;}for (int i = 1; i <= 9; i ++ )if (!st[i]){state[u] = i;st[i] = true;dfs(u + 1);st[i] = false;}
}int main()
{cin >> n;dfs(1);cout << cnt << endl;return 0;
}

AcWing 116. 飞行员兄弟

“飞行员兄弟”这个游戏,需要玩家顺利的打开一个拥有 16 个把手的冰箱。

已知每个把手可以处于以下两种状态之一:打开或关闭。

只有当所有把手都打开时,冰箱才会打开。

把手可以表示为一个 4×4 的矩阵,您可以改变任何一个位置 [i,j] 上把手的状态。

但是,这也会使得第 i 行和第 j 列上的所有把手的状态也随着改变。

请你求出打开冰箱所需的切换把手的次数最小值是多少。

输入格式
输入一共包含四行,每行包含四个把手的初始状态。

符号 + 表示把手处于闭合状态,而符号 - 表示把手处于打开状态。

至少一个手柄的初始状态是关闭的。

输出格式
第一行输出一个整数 N,表示所需的最小切换把手次数。

接下来 N 行描述切换顺序,每行输出两个整数,代表被切换状态的把手的行号和列号,数字之间用空格隔开。

注意:如果存在多种打开冰箱的方式,则按照优先级整体从上到下,同行从左到右打开。

数据范围

1≤i,j≤4

输入样例:

-+--
----
----
-+--

输出样例:

6
1 1
1 3
1 4
4 1
4 3
4 4

思路 :

  • 本题比较特殊,可以用代码把所有情况枚举一遍,会发现每种局面的操作方案是唯一的,所以第一次找到的解一定是最优解。
  • 216∗16∗162^{16}*16*16216∗16∗16
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;typedef pair<int, int> PII;char g[4][4], backup[4][4];int get(int x, int y)
{return x * 4 + y;
}void turn_one(int x, int y)
{if (g[x][y] == '+') g[x][y] = '-';else g[x][y] = '+';
}void turn_all(int x, int y)
{for (int i = 0; i < 4; i ++ ){turn_one(x, i);turn_one(i, y);}turn_one(x, y);
}int main()
{for (int i = 0; i < 4; i ++ ) cin >> g[i];vector<PII> res;for (int op = 0; op < (1 << 16); op ++ ){memcpy(backup, g, sizeof backup);vector<PII> temp;for (int i = 0; i < 4; i ++ )for (int j = 0; j < 4; j ++ )if (op >> get(i, j) & 1){turn_all(i, j);temp.push_back({i, j});}bool has_closed = false;for (int i = 0; i < 4; i ++ )for (int j = 0; j < 4; j ++ )if (g[i][j] == '+')has_closed = true;if (has_closed == false){if (res.empty() || res.size() > temp.size()) res = temp;}memcpy(g, backup, sizeof backup);}cout << res.size() << endl;for (auto op : res) cout << op.first + 1 << ' ' << op.second + 1 << endl;
}

AcWing 1208. 翻硬币

小明正在玩一个“翻硬币”的游戏。

桌上放着排成一排的若干硬币。我们用 * 表示正面,用 o 表示反面(是小写字母,不是零)。

比如,可能情形是:oo*oooo

如果同时翻转左边的两个硬币,则变为:oooo***oooo

现在小明的问题是:如果已知了初始状态和要达到的目标状态,每次只能同时翻转相邻的两个硬币,那么对特定的局面,最少要翻动多少次呢?

我们约定:把翻动相邻的两个硬币叫做一步操作。

输入格式
两行等长的字符串,分别表示初始状态和要达到的目标状态。

输出格式
一个整数,表示最小操作步数

数据范围
输入字符串的长度均不超过100。
数据保证答案一定有解。

输入样例1:

**********
o****o****

输出样例1:

5

输入样例2:

*o**o***o***
*o***o**o***

输出样例2:

1

思路 :

  • 规则是每次翻转两个相邻的硬币,一个硬币要翻转必会使左右两边其中一个受到牵连,因此可以贪心单一方向进行牵连,就不会使之前翻好的硬币重新翻转
#include <iostream>
#include <cstring>
using namespace std;const int N = 110;int n;
char s1[N], s2[N];void turn(int i)
{if (s1[i] == '*') s1[i] = 'o';else s1[i] = '*';
}int main()
{cin >> s1 >> s2;n = strlen(s1);int res = 0;for (int i = 0; i < n; i ++ )if (s1[i] != s2[i]){turn(i), turn(i + 1);res ++ ;}cout << res << endl;
}

蓝桥杯C++ AB组辅导课 第一讲 递归与递推 Acwing相关推荐

  1. 蓝桥杯C++ AB组辅导课 第二讲 二分与前缀和 Acwing

    例题 AcWing 789. 数的范围 给定一个按照升序排列的长度为 n 的整数数组,以及 q 个查询. 对于每个查询,返回一个元素 k 的起始位置和终止位置(位置从 0 开始计数). 如果数组中不存 ...

  2. AcWing 蓝桥杯AB组辅导课 01、递归与递推

    文章目录 前言 一.递归 知识点 例题 题目1:AcWing 95.斐波那契数列[简单,递归写法] 题目2:AcWing 92.递归实现指数型枚举[简单] 题目3:AcWing 94.递归实现排列型枚 ...

  3. 【AcWing】蓝桥杯C++ AB组辅导课

    蓝桥杯 C++ AB 组辅导课 教学计划与递归 如何准备蓝桥杯比赛 做题的套路 第一讲 递归与递推 递归实现指数型枚举 递归实现排列型枚举 递归实现组合型枚举 AcWing116.飞行员兄弟 第二讲 ...

  4. 蓝桥杯C++ AB组辅导课

    整理的算法模板合集: ACM模板 今天在AcWing闲逛白嫖到了yxc老师的蓝桥杯C++ AB组辅导课的题单,正好快要蓝桥杯了,我准备每天花半个小时刷5道这个题单里的水题,练一练,不然到时候我各种花里 ...

  5. 蓝桥杯C++ AB组辅导课 第六讲 双指针、BFS与图论 Acwing

    例题 AcWing 1238. 日志统计 小明维护着一个程序员论坛.现在他收集了一份"点赞"日志,日志共有 N 行. 其中每一行的格式是: ts id 表示在 ts 时刻编号 id ...

  6. AcWing蓝桥杯AB组辅导课08、数论

    文章目录 前言 一.数论 例题 例题1:AcWing 1246. 等差数列(最大公约数,第十届蓝桥杯省赛C++B第7题) 分析 题解:最大公约数 例题2:AcWing 1295. X的因子链(算数基本 ...

  7. AcWing蓝桥杯AB组辅导课10、疑难杂题

    文章目录 前言 例题1:AcWing 1242. 修改数组(并查集) 分析 题解:单链表式并查集 例题2:AcWing 1234. 倍数问题(背包问题+贪心) 分析 题解1:01背包问题,三维解法(贪 ...

  8. AcWing蓝桥杯AB组辅导课07、贪心

    文章目录 前言 一.贪心 模板题 例题1:AcWing 104. 货仓选址(贪心,简单,算法竞赛进阶指南) 分析 题解:贪心思路 例题 例题1:AcWing 1055. 股票买卖 II(贪心.状态机, ...

  9. 蓝桥杯C/C++ AB组辅导课

    文章目录 注意 algorithm 字符串 剪枝 暴力常识 1S时间复杂度 数论 暴力流打法 二进制巧用 归并排序 并查集(查找图中的环) 线段树 大数 国赛准备注意事项 还愿 后记 注意 注意数据范 ...

最新文章

  1. 2022-2028年中国动力电池回收行业投资分析及前景预测报告
  2. LeetCode weekly contest 190 周赛
  3. 中文分词器分词效果的评测方法
  4. [转]java二维码生成与解析代码实现
  5. 无符号数、有符号数、补码在汇编中的运用及相关注意事项
  6. c++基础学习(05)--(指针,引用)
  7. 云原生数据湖解决方案打破数据孤岛,大数据驱动互娱行业发展
  8. 一个程序员的真实生活状态。没错,是我!
  9. Vue的router导航重复-报错:Uncaught (in promise) NavigationDuplicated: Avoided redundant navigation to curren
  10. Java监听器Listener使用说明
  11. Maven最佳实践:管理依赖
  12. Qt在VS2012中引用QtWidgets时报GLES2/gl2.h无法打开错误的解决办法
  13. 看拉扎维《模拟CMOS集成电路设计》的一些总结和思考(九)——运算放大器
  14. [软件笔试] 2014暴风影音校招技术笔试题(长春站)
  15. Java数组,集合,列表的使用与区别
  16. 【总结,持续更新】java常见的线程不安全,你以为的线程安全
  17. 树莓派(Raspberry Pi OS)操作系统的选择
  18. Linux下USB设备自动复位的软件实现
  19. 独家 | 哪个更好:一个通用模型还是多个专用模型?
  20. script 中的src

热门文章

  1. SAP HANA会代替BW吗?
  2. 初级ABAPer考题
  3. UP_DOWN_REQUEST
  4. 銷售訂單 (Sales Order): 資料表及更新記錄
  5. 释疑のCONTEXTS
  6. 距离剩者为王,服饰企业还要跨过很多道坎
  7. 写给计算机老师的一封信800,写给老师的一封信800字作文(一)
  8. Python报错UnicodeEncodeError: ‘ascii‘ codec can‘t encode characters in position 1413-1418: ordinal not
  9. 详细介绍Python中的“魔术方法“__XXX___; 概述__str__()方法;__new__()方法; 三. __ new__ 和__init__的区别
  10. python教程:getattr函数和hasattr函数的用法