蓝桥杯 | 递归 2/24

这篇博客主要讲解了递归算法在蓝桥杯比赛中的应用,包括如何解决回形取数问题和实现组合型枚举。详细介绍了回形取数的解题思路和递归实现,以及递归和循环的关系,并通过实例展示了如何使用递归方法和非递归方法(栈)解决组合型枚举问题。
摘要由CSDN通过智能技术生成

时间:2021/2/24

测试练习

回形取数(蓝桥杯 基础练习)

问题描述
  回形取数就是沿矩阵的边取数,若当前方向上无数可取或已经取过,则左转90度。一开始位于矩阵左上角,方向向下。
输入格式
  输入第一行是两个不超过200的正整数m, n,表示矩阵的行和列。接下来m行每行n个整数,表示这个矩阵。
输出格式
  输出只有一行,共mn个数,为输入矩阵回形取数得到的结果。数之间用一个空格分隔,行末不要有多余的空格。
样例输入
3 3
1 2 3
4 5 6
7 8 9
样例输出
1 4 7 8 9 6 3 2 5
样例输入
3 2
1 2
3 4
5 6
样例输出
1 3 5 6 4 2

int dx[ ] = {1, 0, -1, 0}, dy[ ] = {0, 1, 0, -1}; 下 右 上 左
int dx[ ] = {-1, 0, 1, 0}, dy[ ] = {0, 1, 0, -1}; 上 右 下 左

#include <iostream>
#include <cstdio>
using namespace std;

const int N = 210;
int m, n;
int g[N][N];
bool st[N][N];

int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};//下 右 上 左 
//int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int main(){
	cin >> m >> n;
	for(int i = 0; i < m; i ++ )
	    for(int j = 0; j < n; j ++ )
	        scanf("%d", &g[i][j]);
	
	int mn = m * n;
	int x = 0, y = 0, c = 0, a, b;
	
	
	while(mn --){
		printf("%d ", g[x][y]);
	    st[x][y] = true;
	    
	    a = x + dx[c], b = y + dy[c];
		if(a < 0 || a >= m || b < 0 || b >= n || st[a][b]){
			c = (c + 1) % 4;
			a = x + dx[c], b = y + dy[c];
		}
	    
	    x = a, y = b;
		    
	}        
	return 0;
}

视频学习

蓝桥杯算法教学与培训 1.递归与循环

构造相似性(不能相似的原因可能是缺少参数)
被调函数恰为主调函数
每次调用的层次不同
每次返回的形参并非同一变量
注意返回的次序

递归实现组合型枚举(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
输入样例:
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

1.递归方法

#include <iostream>
#include <cstdio>
using namespace std;

int n, m;

void dfs(int pi, int s, int state){
    //state相当于一个容器,存放选了哪些数 
    //pi表示现在正在看哪个数
	//s 表示当前选了多少个数
	  
	if(s + n - pi + 1 < m)  return;
	//如果从现在开始后面的数都选上也不够 m 个,就不用看这次了,之间退出 
	//同时这个判断条件也包含了 if(pi == n + 1) 的判断,因为 s 一达到 m 就不再递归了
	
	if(s == m){
	    for(int i = 1; i <= n; i ++ )
	        if(state >> i & 1)
	            printf("%d ", i);
	    puts("");
        return;//
    }

	//选

	dfs(pi + 1, s + 1, state | 1 << pi);//把state中的第pi + 1位置成1 
    //不选
	dfs(pi + 1, s, state); 
}

int main(){
	cin >> n >> m;
	dfs(1, 0, 0);

	return 0;
}

解释 state | 1 << pi
<< 的优先级大于 |
例:假设已选的数是 2,3,那么这次传进来的 state 就是 0000 1100
现在要选5
pi = 5
1 << pi (1左移 n 位):
0000 0001 → 0010 0000
与 state 或运算 state | (1 << pi) :
0010 0000
0000 1100
或运算得
0010 1100 那么下一轮递归要传入的 state 就是 0010 1100
这样就表示已选 2 3 5
(pi 要是从 0 开始看,就是 0001 0110 ,选了哪个数,state 的哪一位就是 1,对于 state 来说貌似更直观一些,)

//pi 从 0 开始看
#include <iostream>
#include <cstdio>
using namespace std;

int n, m;

void dfs(int pi, int s, int state){
    //state相当于一个容器,存放选了哪些数 
    //pi表示现在正在看哪个数
	//s 表示当前选了多少个数
	  
	if(s + n - pi < m)  return;
	//如果从现在开始后面的数都选上也不够 m 个,就不用看这次了,之间退出 
	//同时这个判断条件也包含了 if(pi == n + 1) 的判断 
	
	if(s == m){
	    for(int i = 0; i < n; i ++ )
	        if(state >> i & 1)
	            printf("%d ", i + 1);
	    puts("");
        return;//
    }

	//选
	dfs(pi + 1, s + 1, state | 1 << pi);//把state中的第pi + 1位置成1 
    //不选
	dfs(pi + 1, s, state); 
}

int main(){
	cin >> n >> m;
	dfs(0, 0, 0);

	return 0;
}

2. 非递归方法

用栈来代替递归函数 实际上计算机中的递归函数也是通过栈实现的

pi 的每次遍历都分为不选和选两种情况
由于栈是先进先出的,且先看的情况后看不选的情况,所以先放不选后放

每次遍历到某个结点 pi 时
左分枝是选下一个点 p1 + 1,右分枝是空的表示不选p1 + 1
直到某点满足某个判断条件后结束本次循环
回溯到上一个点 pi’ 的右分枝,又分为选 pi’ + 1 和不选 pi’ + 1 两种情况

将递归函数转化为循环具体步骤:

  • 有几个分枝就分为几个状态 这道题中有两个状态 0 1

  • 若是状态 0,执行状态 0 的操作并保证下一个点的状态 0 和这个点的状态 1 入栈

  • 若是状态 1,执行状态 1 的操作并保证下一个点的状态 0 入栈

当然也可分为三种状态 加上代码中注释掉的部分

#include <iostream>
#include <cstdio>
#include <stack>
using namespace std;

struct State{
	int pos, pi, s, state;
};
int n, m;

int main(){
	cin >> n >> m;
	//dfs(0, 0, 0);
	stack<State> stk;
	stk.push({0, 0, 0, 0});
    
    while(stk.size()){
    	State t = stk.top();
    	stk.pop();
    	
    	if(t.pos == 0){
    		if(t.s + n - t.pi < m) continue;
	        if(t.s == m){
	            for(int i = 0; i < n; i ++ )
	                if(t.state >> i & 1)
	                    printf("%d ", i + 1);
	            puts("");
                continue;//结束本次循环
            }
            
            t.pos = 1;
            stk.push(t);//不选 
            stk.push({0, t.pi + 1, t.s + 1, t.state | 1 << t.pi});
	    }
	    
		else if(t.pos == 1){
			//t.pos = 2;
			//stk.push(t);
			stk.push({0, t.pi + 1, t.s, t.state});
		}
			
		//else continue;
     
	}//while
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值