[算法][递归/深搜]递归实现指数型枚举

递归实现指数型枚举

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

输入格式
输入一个整数 n

输出格式
每行输出一种方案。

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

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

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

数据范围
1≤n≤15

输入样例:
3
输出样例:


3
2
2 3
1
1 3
1 2
1 2 3

解题思路
思路一
位掩码法

# 输入整数n
n = int(input())

# 遍历所有可能的子集掩码,每个掩码对应一个二进制数,范围是0到2^n - 1
for mask in range(2 ​**​ n):
    subset = []  # 初始化当前子集
    # 遍历每一位,检查是否选中该位对应的数字
    for i in range(n):
        # 1 << i 生成一个二进制数,仅在第i位为1(从0开始计数)
        # mask & (1 << i) 检查mask的第i位是否为1
        # 如果为真,说明当前数字(i+1)被选中,加入子集
        if mask & (1 << i):
            subset.append(str(i + 1))  # 注意数字是i+1,因为i从0开始
    # 将子集中的元素用空格连接成字符串输出,空子集时输出空行
    print(' '.join(subset))

代码逻辑解释:

  1. 每个子集可以唯一对应一个二进制数(掩码),每一位的0/1表示对应数字是否被选中。
    • 例如,n=3时,掩码5(二进制101)对应子集{1,3}。
  2. 遍历所有可能的掩码(0到2^n-1),每个掩码生成一个子集。
  3. 对于每个掩码,通过位运算检查每一位是否为1:
    • 外层循环遍历所有可能的子集,内层循环遍历每个数字的位置。
    • 由于i从0到n-1递增,对应的数字是1到n,按顺序检查是否选中,保证子集元素升序排列。
  4. 输出时,将子集元素转换为字符串并用空格分隔,空子集自动处理为有效空行。

该方法的时间复杂度为O(n * 2^n),适用于n≤15的情况,完全满足题目要求。

个人理解:
进行 0 到 2^n 的遍历 (即每个数字的二进制码)
e.g: n = 3
for mask in range(2**n):      即是从0到8遍历
for i in range(n):      即是从0到4又遍历一遍
详细说说 1 << i

是 1 向左推出i位
mask在此处已自动变成二进制     0<= i <= 2    0<=mask <=8
mask & (1 << i)是指检查mask的第i位是否为1
当mask = 0 时:

|i	|1<<i	|mask & (1<<i)	|
|0	|1	|0		|
|1	|10	|0		|
|2	|100	|0		|

subset = []

当mask = 1(1)时

|i	|1<<i	|mask & (1<<i)	|
|0	|1	|1		|
|1	|10	|0		|
|2	|100	|0		|

subset = [1]

当mask = 2(10)时

|i	|1<<i	|mask & (1<<i)	|
|0	|1	|0		|
|1	|10	|1		|
|2	|100	|		|

subset = [2]

当mask = 3(11)时

|i	|1<<i	|mask & (1<<i)	|
|0	|1	|1		|
|1	|10	|1		|
|2	|100	|0		|

subset = [1 2]

当mask = 4(100)时

|i	|1<<i	|mask & (1<<i)	|
|0	|1	|0		|
|1	|10	|0		|
|2	|100	|1		|

subset = [3]

当mask = 5(101)时

|i	|1<<i	|mask & (1<<i)	|
|0	|1	|1		|
|1	|10	|0		|
|2	|100	|1		|

subset = [1 3]

当mask = 6(110)时

|i	|1<<i	|mask & (1<<i)	|
|0	|1	|0		|
|1	|10	|1		|
|2	|100	|1		|

subset = [2 3]

当mask = 7(111)时

|i	|1<<i	|mask & (1<<i)	|
|0	|1	|1		|
|1	|10	|1		|
|2	|100	|1		|

subset = [1 2 3]

# 将子集中的元素用空格连接成字符串输出,空子集时输出空行
print(' '.join(subset))

思路二
DFS+递归

lt = [0] * 16
# dfs需要考虑返回情况
def dfs(u):
    if u > n: # n为输入的值(递归深度)
	# 满足条件的情况
        print() # 换行
        for i in range(1,n+1): # [1~n]
            if lt[i]: # 不为0
                print(i,end=" ")

        return
    else:
        lt[u] = 1 #为1的情况遍历
        dfs(u+1)

        lt[u] = 0 #为0的情况遍历
        dfs(u+1)

n = int(input())

dfs(1)

递归树
若n=2
dfs(1)
├─ lt[1]=1 → dfs(2)
│ ├─ lt[2]=1 → dfs(3) → 输出1 2
│ └─ lt[2]=0 → dfs(3) → 输出1
└─ lt[1]=0 → dfs(2)
├─ lt[2]=1 → dfs(3) → 输出2
└─ lt[2]=0 → dfs(3) → 输出空集

posted @ 2025-03-01 00:25  zoom&3  阅读(45)  评论(1)    收藏  举报