时间: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;
}