🚀个人主页:为梦而生~ 关注我一起学习吧!
💡专栏:算法题、 基础算法~赶紧来学算法吧
💡往期推荐:
【算法基础 & 数学】快速幂求逆元(逆元、扩展欧几里得定理、小费马定理)
【算法基础】深搜
递归实现指数型枚举
从 1∼n 这 n 个整数中随机选取任意多个,输出所有可能的选择方案。
输入格式
输入一个整数 n。
输出格式
每行输出一种方案。
同一行内的数必须升序排列,相邻两个数用恰好 1
个空格隔开。
对于没有选任何数的方案,输出空行。
数据范围
1
≤
n
≤
15
1≤n≤15
1≤n≤15
输入样例:
3
输出样例:
3
2
2 3
1
1 3
1 2
1 2 3
代码:
这里从1~n,依次考虑每个数选或不选
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int n;
void dfs(int cur, int state){
if(cur > n){
for(int i = 0; i < n; i++)
if(state >> 1 & 1)
}
}
int main(){
cin >> n;
dfs(1);
return 0;
}
数量级小技巧:
2
20
−
1
0
6
2^{20} - 10^{6}
220−106
2
16
=
65536
2^{16} = 65536
216=65536
2
15
=
32768
2^{15} = 32768
215=32768
2
63
−
1
0
18
2^{63} - 10^{18}
263−1018
dfs最重要的是顺序
递归实现排列型枚举
把 1∼n 这 n 个整数排成一行后随机打乱顺序,输出所有可能的次序。
输入格式
一个整数 n。
输出格式
按照从小到大的顺序输出所有方案,每行 1 个。
首先,同一行相邻两个数用一个空格隔开。
其次,对于两个不同的行,对应下标的数一一比较,字典序较小的排在前面。
数据范围
1
≤
n
≤
9
1≤n≤9
1≤n≤9
输入样例:
3
输出样例:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
递归为了方便理解,可以画出递归搜索树
代码:
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 10;
int st[N], vis[N];
int n;
void dfs(int cur){
if(cur == n){
for(int i = 0; i < n; i++)
cout << st[i] << " ";
cout << endl;
return;
}
for(int i = 1; i <= n; i++){
if(vis[i]) continue;
st[cur] = i;
vis[i] = 1;
dfs(cur + 1);
vis[i] = 0;
}
}
int main(){
cin >> n;
dfs(0);
return 0;
}
简单斐波那契
以下数列 0 1 1 2 3 5 8 13 21 ...
被称为斐波纳契数列。
这个数列从第 3 项开始,每一项都等于前两项之和。
输入一个整数 N,请你输出这个序列的前 N 项。
输入格式
一个整数 N。
输出格式
在一行中输出斐波那契数列的前 N
项,数字之间用空格隔开。
数据范围
0
<
N
<
46
0<N<46
0<N<46
输入样例:
5
输出样例:
0 1 1 2 3
代码:
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 50;
int n;
int f[N];
int main(){
cin >> n;
f[1] = 0;
f[2] = 1;
for(int i = 3; i <= n; i++)
f[i] = f[i-1] + f[i-2];
for(int i = 1; i <= n; i++)
cout << f[i] << " ";
cout << endl;
return 0;
}
递归实现组合型枚举
从 1∼n 这 n 个整数中随机选出 m 个,输出所有可能的选择方案。
输入格式
两个整数 n,m ,在同一行用空格隔开。
输出格式
按照从小到大的顺序输出所有方案,每行 1 个。
首先,同一行内的数升序排列,相邻两个数用一个空格隔开。
其次,对于两个不同的行,对应下标的数一一比较,字典序较小的排在前面(例如 1 3 5 7 排在 1 3 6 8 前面)。
数据范围
n
>
0
,
n>0 ,
n>0,
0
≤
m
≤
n
,
0≤m≤n ,
0≤m≤n,
n
+
(
n
−
m
)
≤
25
n+(n−m)≤25
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
思考题:如果要求使用非递归方法,该怎么做呢?
代码:
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 100;
int path[N];
int vis[N];
int n, m;
void dfs(int cur, int start){
if(cur >= m){
for(int i = 0; i < m; i++) cout << path[i] << " ";
cout << endl;
return;
}
for(int i = start; i <= n && !vis[i]; i++){
path[cur] = i;
vis[i] = 1;
dfs(cur + 1, i + 1);
vis[i] = 0;
// path[cur] = 0;
// cout << "回溯之后的:";
// for(int i = 0; i < m; i++) cout << path[i] << " ";
// cout << " vis:";
// for(int i = 0; i <= n; i++) cout << vis[i] << " ";
// cout << endl;
}
}
int main(){
cin >> n >> m;
dfs(0, 1);
return 0;
}
费解的开关
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
0<n≤500
0<n≤500
输入样例:
3
00111
01011
10001
11010
11100
11101
11101
11110
11111
11111
01111
11111
11111
11111
11111
输出样例:
3
2
-1
基本思路:
- 经过操作后发现,从上到下和从下到上操作后得到的结果是一样的,即顺序不影响结果,所以我们可以确定一种从上到下从左到右的操作顺序。
- 由于数据范围比较小,可以使用枚举的方法
- 对于每个开关,有两种操作,按或不按
- 每个位置操作完之后不能再进行操作,按照上面的既定,我们需要一行一行的操作
- 由于操作的特点,第一行操作完之后还是熄灭的灯的下面一行还可以进行操作,如果亮,下面的位置不能进行操作
- 而且最后一行不能进行操作(由上面的推理出来的)
- 因此只需要看前4行的操作,如果最后一行已经好了,证明方案可行,记录最优解
代码:
#include<iostream>
#include<cstring>
#include<algorithm>
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 || b < 0 || a >= 5 || b >= 5) continue;
g[a][b] ^= 1;
}
}
int main(){
int T;
cin >> T;
while(T--){
int min_step = 10;
for(int i = 0; i < 5; i++) cin >> g[i];
//枚举第一行的状态
for(int op = 0; op < 32; op++){
int step = 0;
memcpy(backup, g, sizeof(g));
//对第一行进行相应的操作
for(int i = 0; i < 5; i++)
if(op >> i & 1){
step++;
turn(0, i);
}
//对操作完的情况完成后4行操作
for(int i = 0; i < 4; i++){
for(int j = 0; j < 5; j++){
//如果这个位置是0,在下一行的这个位置操作一下
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;
if(!dark) min_step = min(min_step, step);
memcpy(g, backup, sizeof g);
}
if(min_step > 6) cout << -1 << endl;
else cout << min_step << endl;
}
return 0;
}
作者:为梦而生
链接:https://www.acwing.com/activity/content/code/content/7859582/
来源:AcWing