【ACwing 93】【模版】非递归实现组合型枚举——模拟递归
(题面来自ACwing)
从 1~n 这 n 个整数中随机选出 m 个,输出所有可能的选择方案。
输入格式
两个整数 n,m ,在同一行用空格隔开。
输出格式
按照从小到大的顺序输出所有方案,每行1个。
首先,同一行内的数升序排列,相邻两个数用一个空格隔开。
其次,对于两个不同的行,对应下标的数一一比较,字典序较小的排在前面(例如1 3 5 7排在1 3 6 8前面)。
数据范围
n>0 ,
0≤m≤n ,
n+(n−m)≤25
此题的正解是dfs枚举,在之前的博客中有所提及。现在考虑用循环模拟机器递归的方式来做。
计算机维护系统栈来实现递归,栈中每个元素记录三个状态:当前函数的参数、上一层递归的位置(旧栈顶),以及递归完成后,上一层递归函数应当执行的下一条语句。我们模拟的栈的上一层栈顶位置是显然的,不需要记录第二个参数。
递归的过程是:每次将栈顶的元素出栈,确认它对应的状态执行到了哪一步,从这一步继续向下执行,直到返回或者遇到下一个递归调用。举个例子:下面是此题正解的dfs函数,我们可以把它分为3段,如代码所示。
- void dfs(int x) {
- //--------#0
- if (sel.size() > m || sel.size() + n - x + 1 < m)
- return;
- if (x == n + 1) {
- rep(0, m - 1)
- printf("%d ", sel[i]);
- puts("");
- return;
- }
- sel.push_back(x);
- //--------#0
- dfs(x + 1);
- //--------#1
- sel.pop_back();
- //--------#1
- dfs(x + 1);
- //--------#2
- return;
- }
基于这段函数体,我们用循环来模拟递归调用的过程。定义状态state包含的两个元素为当前函数参数x、当前函数的执行位置;注意这里的状态和系统栈中有差异。系统栈中记录的是返回后函数的执行位置,这里为了更直观地演示当前函数的执行而做了修改。首先将第一个状态{1, 0}入栈(对应主函数中的递归调用入口)。接下来进入循环体,我们在栈不空时循环执行以下语句:
1、出栈,记录栈顶参数x和当前执行位置cur_addr。
2、如果cur_addr为0,说明该函数从头开始执行,我们执行第0段函数体。执行完毕后,我们先入栈{x, 1}(表示再退栈到该状态时应当执行第1段函数体了),仔把下一个函数的参数x+1、下一个函数的执行位置(0)入栈。之后continue进入下一层循环;
3、如果cur_addr为1,说明该执行第二段函数体;这时我们先后入栈{x, 2}(返回到该状态时执行第2段函数体)、{x+1, 0}(下一层递归),继续循环。
4、如果cur_addr为2,当前函数已执行完毕,不用再次入栈。继续循环即可。
完整main函数代码:
- struct State {
- int x, addr;
- State(int a, int b):
- x(a), addr(b) {}
- };
- int main() {
- cin >> n >> m;
- stack<State> sta;
- sta.push(State(1, 0));//dfs(1);
- while (sta.size()) {
- int x = sta.top().x, cur_addr = sta.top().addr;
- sta.pop();
- switch (cur_addr) {
- case 0:
- if (sel.size() > m || sel.size() + n - x + 1 < m)
- continue;
- if (x == n + 1) {
- rep(0, m - 1)
- printf("%d ", sel[i]);
- puts("");
- continue;
- }
- sel.push_back(x);
- sta.push(State(x, 1));
- sta.push(State(x+1, 0));
- continue;
- case 1:
- sel.pop_back();
- sta.push(State(x+1, 0));
- }
- }
- return 0;
- }
算法进阶上给出的代码与系统栈的返回方式更加契合,这里一并给出。
- #include <iostream>
- #include <cstring>
- #include <cstdio>
- #include <vector>
- using namespace std;
- vector<int> chosen;
- int top, address, sta[100010], n, m;
- inline void call(int x, int ret_addr) { //模拟系统栈指令call(),记录每个状态的参数和返回语句位置
- int pre = top;
- sta[++top] = x;
- sta[++top] = ret_addr;
- sta[++top] = pre;
- }
- inline int ret() { //模拟指令return,退栈并返回应该执行的下一条语句
- int ret_addr = sta[top - 1];
- top = sta[top];
- return ret_addr;
- }
- int main() {
- cin >> n >> m;
- call(1, 0);
- while (top) {
- int x = sta[top - 2];
- switch (address) {
- case 0:
- if (chosen.size() > m || chosen.size() + (n - x + 1) < m) {
- address = ret();
- continue;
- }
- if (x == n + 1) {
- for (int i = 0; i < chosen.size(); ++i)
- printf("%d ", chosen[i]);
- puts("");
- address = ret();
- continue;
- }
- chosen.push_back(x);
- call(x+1, 1); //入栈下一个状态
- address = 0; //下一个函数从头执行
- continue;
- case 1:
- chosen.pop_back();
- call(x+1, 2); //入栈下一个状态
- address = 0;
- continue;
- case 2:
- address = ret(); //当前状态已执行完毕,返回
- }
- }
- return 0;
- }
【ACwing 93】【模版】非递归实现组合型枚举——模拟递归的更多相关文章
- AcWing 93. 递归实现组合型枚举
AcWing 93. 递归实现组合型枚举 原题链接 从 1~n 这 n 个整数中随机选出 m 个,输出所有可能的选择方案. 输入格式 两个整数 n,m ,在同一行用空格隔开. 输出格式 按照从小到大的 ...
- ACAG 0x02-8 非递归实现组合型枚举
ACAG 0x02-8 非递归实现组合型枚举 之所以专门来写这道题的博客,是因为感觉从最根本处了解到了递归的机器实现. 主要的就是两个指令--Call和Ret. Call指令会将返回地址入栈(系统栈) ...
- ACWing93.递归实现组合型枚举
题面 \93. 递归实现组合型枚举 从 1∼n 这 n 个整数中随机选出 m 个,输出所有可能的选择方案. 输入格式 两个整数 n,m ,在同一行用空格隔开. 输出格式 按照从小到大的顺序输出所有方案 ...
- 4位组合型Excel文档密码怎么破解
现代社会我们会遇到各种密码,很多的密码我们一段时间不用就不知不觉的忘记了.很多的excel用户就遇到过这种情况,这个时候我们就需要一款Excel密码破解工具.Advanced Office Passw ...
- RxJava 1.x 笔记:组合型操作符
最近去检查眼睛,发现度数又涨了,唉,各位猿多注意保护自己的眼睛吧! 前面学了 RxJava 的三种关键操作符: 创建型操作符 过滤型操作符 变换型操作符 读完本文你将了解第四种(组合型操作符): 组合 ...
- Kubernetes用户指南(二)--部署组合型的应用、连接应用到网络中
一.部署组合型的应用 1.使用配置文件启动replicas集合 k8s通过Replication Controller来创建和管理各个不同的重复容器集合(实际上是重复的pods). Replicati ...
- Untargeted lipidomics reveals specific lipid abnormality in nonfunctioning human pituitary adenomas 非靶向脂质组学揭示非功能人类脑垂体瘤中的特异性脂质 (解读人:胡丹丹)
文献名:Untargeted lipidomics reveals specific lipid abnormality in nonfunctioning human pituitary adeno ...
- AcWing 94. 递归实现排列型枚举
AcWing 94. 递归实现排列型枚举 题目链接 把 1~n 这 n 个整数排成一行后随机打乱顺序,输出所有可能的次序. 输入格式 一个整数n. 输出格式 按照从小到大的顺序输出所有方案,每行1个. ...
- java面试-公平锁/非公平锁/可重入锁/递归锁/自旋锁谈谈你的理解
一.公平锁/非公平锁/可重入锁/递归锁/自旋锁谈谈你的理解 公平锁:多个线程按照申请的顺序来获取锁. 非公平锁:多个线程获取锁的先后顺序与申请锁的顺序无关.[ReentrantLock 默认非公平.s ...
随机推荐
- viewpage轮播图
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com ...
- MongoDB复制 --- MongoDB基础用法(五)
复制 MongoDB复制是将数据同步在多个服务器的过程. 复制提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性, 并可以保证数据的安全性. 复制还允许您从硬件故障和服务中断中恢复 ...
- js-同步和异步
js异步 学习js开发,无论是前端开发还是node.js,都避免不了要接触异步编程这个问题,就和其它大多数以多线程同步为主的编程语言不同,js的主要设计是单线程异步模型.正因为js天生的与众不同,才使 ...
- [Luogu P2257] YY的GCD (莫比乌斯函数)
题面 传送门:洛咕 Solution 推到自闭,我好菜啊 显然,这题让我们求: \(\large \sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)\in prime]\) 根 ...
- 【USACO】Cow Brainiacs
题意描述 Cow Brainiacs 求 \(n!\) 在 \(b\) 进制表示下的第一位非 \(0\) 位的数字. 算法分析 闲话 忙人自动略过 之前做过一道 \(10\) 进制表示下的题目,感觉差 ...
- Docker(8)- docker search 命令详解
如果你还想从头学起 Docker,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1870863.html 作用 从 Docker Hub ...
- Spring Cloud Alibaba 之 user服务
项目技术选型 Spring Boot Spring MVC MyBatis + 通用Mapper (官网信息https://mapperhelper.github.io/docs/) Spring C ...
- 找出"吸血鬼数"(Java)
吸血鬼数是指位数为偶数的数字,可以由一 对数字相乘而得到,而这对数字各包含乘积的一半 位数的数字,其中从最初的数字中选取的数字可以任意排序.以两个0结尾的数字是不允许的,例如,下列数字都是 " ...
- 汉诺塔问题实验--一个简洁的JAVA程序
思路: 这里使用递归法 n==1的时候,直接把它从x移到z位置即可. 如果是n层,我们首先把上面的n- 1层移到y位置,然后把最 下面的那个最大的盘子,移到z位置,然后把y上面放的上面n-1层移到z位 ...
- Flask补充内容
关键字: 一,过滤器 二,增删改查 一,过滤器 1,概念:过滤器的本质就是函数.有时候我们不仅仅只是需要输出变量的值,我们还需要修改变量的显示,甚至格式化.运算等等,而在模板中是不能直接调用 Pyth ...