题目
小明有n个可选运动,每个运动有对应卡路里,想选出其中k个运动且卡路里和为t。k,t,n都是给定的。求出可行解数量
输入描述
第一行输入n t k
第二行输入 每个运动的卡路里 按照空格进行分割
备注
0<n<10
t>0,0<k<=n
每个运动量的卡路里>0
输出描述
求出可行解数量
示例1:
输入
4 3 2
1 1 2 3
输出
2
说明
可行解为2,选取{0,2},{1,2}两种方式。
def find_data():
n, t, k = map(int,input().split())
nums = list(map(int,input().split()))
count_sum = 0
def backstack(start,count,sums):
nonlocal count_sum
if count>k:
return
if sums >t:
return
# 剩余元素不足以选满k个
if count + (n - start) < k:
return
if sums ==t and count==k:
count_sum+=1
return
for i in range(start,n):
backstack(i+1,count+1,sums+nums[i])
backstack(0,0,0)
return count_sum
if __name__ == '__main__':
print(find_data())这个回溯算法的逻辑是寻找数组中满足特定条件的子集个数。让我总结一下核心逻辑:
算法目标
从n个元素的数组中,找出所有恰好包含k个元素且元素和为t的子集个数。
核心逻辑
1. 参数设计
start: 当前搜索的起始索引(避免重复组合)count: 已选择的元素个数sums: 已选择元素的和
2. 剪枝策略(关键优化)
python
# 1. 元素个数超过k → 剪枝
if count > k: return
# 2. 元素和超过t → 剪枝
if sums > t: return
# 3. 剩余元素不足以选满k个 → 剪枝
if count + (n - start) < k: return3. 终止条件
python
if sums == t and count == k: # 找到有效解
count_sum += 1
return4. 搜索过程
从
start开始遍历数组每次选择
nums[i],递归搜索下一个位置i+1采用组合搜索而非排列搜索(通过
start参数保证)
算法特点
组合问题:顺序无关([1,2]和[2,1]视为相同)
精确匹配:要求元素个数恰好为k,和恰好为t
深度优先搜索:递归探索所有可能组合
多重剪枝:有效减少不必要的搜索
时间复杂度
最坏情况:O(C(n,k)),即从n个元素中选k个的所有组合
实际由于剪枝,通常远小于理论值
适用场景
适合解决子集和问题的变体,特别是在n不大(通常n≤30)且需要精确匹配元素个数和总和的情况下。
这是一个典型的回溯+剪枝算法实现,平衡了穷举的完整性和效率优化。
回溯法是一种解决问题的算法框架,用于寻找问题的所有解或最优解。
核心思想:尝试所有可能的选项,如果发现当前路径不可能得到解,就回退一步,尝试其他选项。
关键特征:
做出选择
递归探索
撤销选择(回退到之前的状态)
剪枝(提前终止不可能得到解的路径)
回溯法模板:
python
def backtrack(路径, 选择列表):
if 满足结束条件:
结果.append(路径.copy()) # 注意要复制
return
for 选择 in 选择列表:
# 做选择
路径.append(选择)
从选择列表中移除该选择 # 可选
# 剪枝:如果选择不合法,跳过
if not is_valid(选择):
路径.pop() # 撤销选择
continue
# 递归
backtrack(路径, 选择列表)
# 撤销选择(回溯的核心)
路径.pop()