概念
在搜索过程中,如果需要完全遍历所有情况可能需要很多时间
在搜索到某种状态时,根据当前状态判断出后续无解,则该状态无需继续深入搜索
例如:给定n个正整数,求出有多少个子集之和小于等于k。在搜索过程中当前选择的数字和已经超过k则不需要继续搜索。
可行性剪枝
当前状态和题意不符,并且往后的所有情况和题意都不符,那么可以剪枝
最优性剪枝
在搜索过程中,当前状态已经不如已经找到的最优解,也可以剪枝,不需要继续搜索。
2942数字王国之军训排队
题目链接:2942数字王国之军训排队
dfs搜索:枚举每个学生分到组内
可行性剪枝:满足题目条件
最优性剪枝:判断当前状态是否比ans更劣
# 判断x能否加入group组
def check(x, group):
# 要保证不能存在倍数关系
for y in group:
if x % y == 0 or y % x == 0:
return False
return True
# depth表示当前为第depth个学生
def dfs(depth):
global ans
# 最优性剪枝:当前已经比ans大,说明该策略不可行
if len(Groups) > ans:
return
# 递归深度达到 n(所有学生都分配完毕),则更新最优解并返回。
if depth == n:
ans = min(len(Groups), ans)
return
# 遍历当前已有的组,尝试将第 depth 个学生加入其中,但要通过 check 函数检查是否符合条件。
for each_group in Groups:
# 枚举第depth个学生能否加入当前组each_group
# 剪枝:必须满足题意
if check(a[depth], each_group):
each_group.append(a[depth])
dfs(depth + 1)
each_group.pop()#当一个学生无法加入某个组,或者一个学生加入后需要回溯到之前的状态时,使用 pop 将该学生从组的成员列表中移除。
# 单独作为一组
Groups.append([a[depth]])
dfs(depth + 1)
Groups.pop()
n = int(input())
a = list(map(int, input().split()))
# ans表示最少能分多少队
ans = n
# Groups表示分组情况
Groups = []
dfs(0)
print(ans)
3075特殊的多边形
题目链接:3075特殊的多边形
先考虑乘积为v有多少种n边形
dfs处理出所有乘积对应的所有可能维护一个递增的边长序列(唯一性)
枚举第i边的长度,最小最大范围(剪枝)
最终check是否满足n边形最小的n-1条边之和大于第n边
预处理+前缀和O(1)查询答案
def dfs(depth, last_val, tot, mul):
'''
:param depth:第depth条边长
:param last_val:上一边长长度
:param tot:累计和
:param mul:累计乘积
'''
if depth == n:
# 前n-1条边之和大于第n条边
if tot - path[-1] > path[-1]:
ans[mul] += 1
return
for i in range(last_val + 1, 100001):
# 最优性剪枝,后续还有n-depth个数字,每个数字都要>=i
# 累计乘积要不超过100000:mul*(i*(n-depth))
if mul * (i ** (n - depth)) > 100000:
break
path.append(i)
dfs(depth + 1, i, tot + i, mul * i)
path.pop()
t, n = map(int,input().split())
ans = [0] * 100001
path = []
dfs(0, 0, 0, 1)
for i in range(1, 100001):
ans[i] += ans[i - 1]
for _ in range(t):
l, r = map(int,input().split())
print(ans[r] - ans[l - 1])