• 第一讲 递归和递推


    目录

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

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

    AcWing 717. 简单斐波那契

    AcWing 95. 费解的开关


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

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

    输入格式

    输入一个整数 n。

    输出格式

    每行输出一种方案。

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

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

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

    数据范围

    1≤n≤15

    输入样例:

    3
    

    输出样例:

    1. 3
    2. 2
    3. 2 3
    4. 1
    5. 1 3
    6. 1 2
    7. 1 2 3

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

    1. #include
    2. using namespace std;
    3. int n;
    4. void dfs(int u,int state){
    5. //如果枚举到第n个数
    6. if(u==n){
    7. //就输出前面枚举的结果,利用state来确定第u个数是否被选过
    8. for(int i = 0;i
    9. //将state第i位右移,然后判断是0还是1,如果是1就输出
    10. if(state>>i & 1){
    11. //输出的数是从1开始枚举,而我们的计算是从0开始
    12. cout <1<<" ";
    13. }
    14. }
    15. //输出空格
    16. cout<
    17. return ;
    18. }
    19. //不选下一个数
    20. dfs(u+1,state);
    21. //选下一个数,并在该位标记
    22. dfs(u+1, state|1<
    23. }
    24. int main(){
    25. //输入需要枚举的数的范围,n最大为15,最多情况3万多
    26. //可以用int 4字节的后15个比特位来表示所有的状态
    27. cin>>n;
    28. //数从0开始枚举,第二个参数为0,说明初始化所有的比特位为0
    29. dfs(0,0);
    30. return 0;
    31. }

    state>>i & 1 先将第i位移到第1位然后判断它是0还是1

    state|1<

    94. 递归实现排列型枚举

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

    输入格式

    一个整数 n。

    输出格式

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

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

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

    数据范围

    1≤n≤9

    输入样例:

    3
    

    输出样例:

    1. 1 2 3
    2. 1 3 2
    3. 2 1 3
    4. 2 3 1
    5. 3 1 2
    6. 3 2 1
    1. #include
    2. #include
    3. using namespace std;
    4. int n;
    5. //相当于c语言里面的一维数组
    6. vector<int> path;
    7. void dfs(int u,int state){
    8. if(u==n){
    9. //和java里面的增强for循环一样。到了南墙,输出所有的数。
    10. for(auto i:path)cout<" ";
    11. cout<
    12. return;
    13. }
    14. for(int i=0;i
    15. //判断第i个数是否放在过坑里
    16. //先把第i个位比特位移到第一位然后看他是0是1
    17. //表示第i个数是否被使用过
    18. if(!(state>>i&1)){
    19. //如果没有使用过就入坑
    20. path.push_back(i+1);
    21. //搜索下一个数,然后把state的第i位标记为1,表示第i个数已经被使用过了
    22. dfs(u+1,state|1<
    23. //回溯,撞到南墙该回头了。
    24. path.pop_back();
    25. }
    26. }
    27. }
    28. int main(){
    29. cin>>n;
    30. //param1:从0开始搜索到n-1
    31. //param2:二进制数表示哪些数被用过了,这里表示第一个数没有被用过开始搜索。
    32. dfs(0,0);
    33. return 0;
    34. }

    717. 简单斐波那契

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

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

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

    输入格式

    一个整数 N。

    输出格式

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

    数据范围

    0

    输入样例:

    5
    

    输出样例:

    0 1 1 2 3

    递推做法

    1. #include
    2. using namespace std;
    3. int main(){
    4. int n;
    5. //数据范围
    6. int a[46];
    7. cin>>n;
    8. //初始条件
    9. a[0] = 0;
    10. a[1] = 1;
    11. //递推
    12. for(int i=2;i
    13. //递推公式
    14. a[i] = a[i-1]+a[i-2];
    15. }
    16. for(int i=0;i" ";
    17. return 0;
    18. }

    优化:滚动数组的雏形,节省空间

    1. #include
    2. using namespace std;
    3. int main(){
    4. int n;
    5. cin>>n;
    6. //初始条件
    7. int a = 0,b = 1;
    8. //递推
    9. for(int i=0;i
    10. //递推公式
    11. cout<< a <<" ";
    12. //a 和 b 同时往后错位
    13. //存好第三个数
    14. //让a等于第二个数
    15. //让b等于第三个数
    16. int fn = a+b;
    17. a = b;
    18. b = fn;
    19. }
    20. return 0;
    21. }

    AcWing 95. 费解的开关

    你玩过“拉灯”游戏吗?

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

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

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

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

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

    下面这种状态

    1. 10111
    2. 01101
    3. 10111
    4. 10000
    5. 11011

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

    1. 01111
    2. 11101
    3. 10111
    4. 10000
    5. 11011

    再改变它正中间的灯后状态将变成:

    1. 01111
    2. 11001
    3. 11001
    4. 10100
    5. 11011

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

    输入格式

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

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

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

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

    输出格式

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

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

    数据范围

    0

    输入样例:

    1. 3
    2. 00111
    3. 01011
    4. 10001
    5. 11010
    6. 11100
    7. 11101
    8. 11101
    9. 11110
    10. 11111
    11. 11111
    12. 01111
    13. 11111
    14. 11111
    15. 11111
    16. 11111

    输出样例:

    1. 3
    2. 2
    3. -1
    1. #include
    2. #include
    3. using namespace std;
    4. //先把大体框架搭出来,然后再实现细节的函数
    5. //取名最好见名知意,debug的时间往往要多于写代码
    6. int const INF = 1000000;
    7. char g[10][10];
    8. //这两个一维数组表示本身和周围共5个位置
    9. int dx[5] = {0,-1,0,1,0} , dy[5] = {0,0,1,0,-1};
    10. //功能:将某个下标和周围的数取反
    11. void turn(int x,int y){
    12. int a,b;
    13. //遍历周围的五个方向,分别取反
    14. for(int i = 0;i<5;i++){
    15. //a和b为偏移后位置的下标
    16. a = x + dx[i];
    17. b = y + dy[i];
    18. //如果偏移后的位置没有越界就取反
    19. if(a>=0&&a<5&&b>=0&&b<5){
    20. g[a][b] ^= 1;
    21. }
    22. }
    23. }
    24. int work(){
    25. //这个数组用来做备份,来存储g的原来状态
    26. char backup[10][10];
    27. //等于一个较大的数
    28. int ans = INF;
    29. //将g复制到backup里,cstring头文件中
    30. memcpy(backup, g, sizeof g);
    31. //遍历第一行的全部32种情况,对第一行进行的操作,决定了后面几行的处理情况。
    32. //只有将第一行按压灯泡的所有可能情况都取到,才能补充不漏的找到最少的操作数。
    33. for(int k = 0;k<1<<5;k++){
    34. //开始一种新的情况
    35. int res = 0;
    36. //如果有1就按压,这里的1不代表灯泡的状态,而是代表是否按压灯泡
    37. //第i位是否是1,如果是1就进行按压,操作数增加
    38. for(int i = 0;i<5;i++){
    39. if(k>>i&1){
    40. res++;
    41. turn(0, i);
    42. }
    43. }
    44. //上面是对第一行进行操作后的初始状态,接下来除最后一行,每一行都跟着进行处理
    45. //如果某个位置是0,就把这个位置下方的数按压,让这个位置的数变成1
    46. for(int i = 0;i<4;i++){
    47. for(int j = 0;j<5;j++){
    48. if(g[i][j]=='0'){
    49. res++;
    50. turn(i+1, j);
    51. }
    52. }
    53. }
    54. //利用最后一行判断是否处理成功,也就是所有灯泡都是亮的
    55. bool is_successful = true;
    56. for(int i = 0;i<5;i++){
    57. if(g[4][i]!='1'){
    58. is_successful = false;
    59. break;
    60. }
    61. }
    62. //获得32种情况中,满足条件的最小的数
    63. if(is_successful) ans = min(ans,res);
    64. //一种情况计算完毕,将g重新复原,进行下一个状态的计算
    65. memcpy(g, backup, sizeof g);
    66. }
    67. //题目要求,如果操作数大于6输出-1
    68. if(ans>6) ans = -1;
    69. return ans;
    70. }
    71. int main(){
    72. int T;
    73. cin>>T;
    74. while(T--){
    75. //因为是二维的字符数组,所以可以一行行输入
    76. for(int i = 0;i<5;i++) cin>>g[i];
    77. cout<<work()<
    78. }
    79. return 0;
    80. }

  • 相关阅读:
    解锁C语言结构体的力量(初阶)
    【cmake开发(8)】cmake 编译无法找到库,和编译通过后运行时无法找到库
    NVME2.0协议——新特性
    vue——基于element嵌套表格+多选
    计算机毕业设计Java优课网设计与实现(系统+程序+mysql数据库+Lw文档)
    从源码分析 MGR 的新主选举算法
    数据库设计:防止MySQL字段名与关键字相撞,保护数据完整性!
    探索DeFi元宇宙:NFT、Web3和DAPP的数藏Swap合约应用开发
    第04章 第04章 队列
    172.mybatisPlus的实际应用
  • 原文地址:https://blog.csdn.net/qq_61735602/article/details/127299070