Acwing:蓝桥杯备考第一天--递归与递推

1.递归实现指数型枚举(排列组合,2^n)

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

输入格式

输入一个整数 n。

输出格式

每行输出一种方案。

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

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

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

原题链接:92. 递归实现指数型枚举 - AcWing题库

 讲解:递归(dfs)要注意顺序,保证每一种可能都按序输出。

此题的递归思想可简化为选与不选,以树的形式去排序并回溯(递归搜索树)。有位运算和数组两种方法。需要记录状态:选、未选、暂未考虑

1.1位运算法(状态压缩递归)

#include<bits/stdc++.h>
using namespace std;
int n;
//用二进制状态压缩,也就是用二进制上的位来记录数有没有被用过。
// u是当前枚举到的数,state是二进制数记录哪些数被选
void dfs(int u, int state) {//u是位数,state是状态,从第0个数开始,并记录状态
	if (u == n) {//已经枚举到最后一位,达到边界
		for (int i = 0; i < n; i++)
			if (state >> i & 1)//把state右移i位,即把i移到个位,判断是否为1
				cout << i + 1 << " ";
		cout << endl;
		return;
	}
	dfs(u + 1, state);//不选,state不变
    // 用这个数,把第u位变成1
    // 运算优先级: 左移高于位运算|
	dfs(u + 1, state | 1 << u);//选择,把state的第u位置成1
}

int main()
{
	cin >> n;
	//选与不选,dfs
	dfs(0, 0);//从第0位到N-1位
}

状态压缩的特性:可以枚举所有选与不选的情况

题目要求的结果是2^n个
这2^n个选择情况,对应于一个n位的2进制数的各个位取0或取1的情况。
例中n=3,即
000 -> \n
001 -> 1
010 -> 2
100 -> 3
011 -> 1 2
101 -> 1 3
110 -> 2 3
111 -> 1 2 3

1.2数组法

#include<bits/stdc++.h>
using namespace std;
int n;
const int N = 16;
int st[N];//状态,记录每个位置当前状态,0表示还未考虑,1选,2不选

void dfs(int u) {
	if (u > n) {
		for (int i = 1; i <= n; i++) {
			if (st[i] == 1)
				cout << i << " ";
		}
		cout << endl;
		return;
	}
	st[u] = 2;//记录的0,1,2类似于空,√,×
	dfs(u + 1);//第一个分支,不选
	st[u] = 0;//回溯

	st[u] = 1;
	dfs(u + 1);//第二个分支,选
	st[u] = 0;
}


int main()
{
	cin >> n;
	//选与不选,dfs
	dfs(1);
}

 dfs(深度优先搜索)

2. 递归实现排列型枚举(无重复元素的全排列)

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

输入格式

一个整数 n。

输出格式

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

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

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

数据范围

1≤n≤9

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>

using namespace std;

const int N = 10;

int n;
int state[N];//状态(输出的方案)
bool used[N];//判断当前位置可以用的数有哪些,即数有没有被用过  true用过,false未用

void dfs(int u) { 
	if (u > n) {   //边界,位置都设置好了,输出数
		for (int i = 1; i <= n; i++) {
			cout << state[i] << " ";
		}
		cout << endl;
		return;
	}

	//枚举每个分支,即当前位置可以填哪些数(更新状态)
	for (int i = 1; i <= n; i++) {
		if (!used[i]) {  //当前数未被用过
			state[u] = i;//第u个位子放入第i个数,放入空位
			used[i] = true;
			dfs(u + 1);//填下一个位

			//回溯
			state[u] = 0;
			used[i] = false;
		}
	}
}



int main()
{
	cin >> n;
	dfs(1);//当前枚举到第几位
	return 0;
}

顺序1:依次枚举每个数,放哪个位置

顺序2:依次枚举每个位置,放哪个数

字典序:从小到大排

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值