DFS
def dfs(depth):
#depth为当层第几重循环即深度
if depth==N:
#当到达边界是,即返回
return
#每重循环进行枚举选择,即dfs递归处
回溯
求全排列
def dfs(depth):
if depth==n:
print(path)
return
for i in range(1,n+1):
if vis[i]:
continue
vis[i]=1
path.append(i)
dfs(depth+1)
vis[i]=0
path.pop(-1)
n=int(input())
vis=[0]*(n+1)
path=[]
dfs(0)
N皇后问题
题目描述
在 N×NN\times NN×N 的方格棋盘放置了 NNN 个皇后,使得它们不相互攻击(即任意 222 个皇后不允许处在同一排,同一列,也不允许处在与棋盘边框成 454545 角的斜线上。你的任务是,对于给定的 NNN,求出有多少种合法的放置方法。
输入描述
输入中有一个正整数 N≤10N≤10N≤10,表示棋盘和皇后的数量
输出描述
为一个正整数,表示对应输入行的皇后的不同放置数量。
import sys
input=lambda:sys.stdin.readline().strip()
ans=0
def dfs(x):
if x==n+1:
global ans
ans+=1
return
for y in range(1,n+1):
if vis1[y] or visa[x+y] or visb[x-y]:
continue
vis1[y]=visa[x+y]=visb[x-y]=True
dfs(x+1)
vis1[y]=visa[x+y]=visb[x-y]=False
n=int(input())
vis1=[False]*(n+1)
visa=[False]*(2*n+1)
visb=[False]*(2*n+1)
dfs(1)
print(ans)
剪枝
数学王国之军训排队
问题描述
数字王国开学了,它们也和我们人类一样有开学前的军训,现在一共有 nnn 名学生,每个学生有自己的一个名字 aia_iai(数字王国里的名字就是一个正整数,注意学生们可能出现重名的情况),此时叛逆教官来看了之后感觉十分别扭,决定将学生重新分队。
排队规则为:将学生分成若干队,每队里面至少一个学生,且每队里面学生的名字不能出现倍数关系(注意名字相同也算是倍数关系)。
现在请你帮忙算算最少可以分成几队?
例:有 444 名学生 (2,3,4,4)(2,3,4,4)(2,3,4,4),最少可以分成 (2,3)(2,3)(2,3)、(4)(4)(4)、(4)(4)(4) 共 333 队。
输入格式
第一行包含一个正整数 nnn,表示学生数量。
第二行包含 nnn 个由空格隔开的整数,第 iii 个整数表示第 iii 个学生的名字 aia_iai。
输出格式
输出共 111 行,包含一个整数,表示最少可以分成几队。
样例输入
4 2 3 4 4
样例输出
3
n = int(input())
a = list(map(int, input().split()))
def dfs(x, value):
vis[x] = 1
for i in range(x + 1, n):
if vis[i] == 0 and a[i] % value != 0:
dfs(i, value)
ans = 0
vis = [0] * n
for i in range(n):
if vis[i] == 0:
dfs(i, a[i])
ans+=1
print(ans)
import sys
sys.setrecursionlimit(100000)
n = int(input())
a = list(map(int,input().split()))
ans = n
# 判断当前这个学生是否能进入
def check(x,group):
for i in group:
if x % i == 0 or i % x == 0:
return False
return True
# 分组
groups = []
def dfs(x):
# x: 当前是第几个学生
global ans
if len(groups) > ans:
return
if x == n:
ans = min(ans,len(groups))
return
# 枚举当前学生是否能加入到已有的组内
for i in range(len(groups)):
if check(a[x],groups[i]):
groups[i].append(a[x])
# 当前学生添加进来之后,就要去看下一个学生了
# 这个地方是回溯,也就是这个学生添加到这个组之后,去看下一个组可不可以
# 其实就是让当前这个人进去每个组试试
dfs(x + 1)
# 回溯删除
groups[i].pop()
# 这是第二种情况,对于每个人来说,都有单独开一组的权利
groups.append([a[x]])
# 一样要回溯
dfs(x + 1)
groups.pop()
dfs(0)
print(ans)
特殊的多边形
问题描述
假设一个 nnn 边形 nnn 条边为 a1,a2,a3,⋯ ,ana_1,a_2,a_3,\cdots,a_na1,a2,a3,⋯,an,定义该 nnn 边形的值 v=a1×a2×a3×⋯×anv = a_1 \times a_2 \times a_3 \times \cdots \times a_nv=a1×a2×a3×⋯×an。
定义两个 nnn 边形不同是指至少有一条边的长度在一个 nnn 边形中有使用而另一个 nnn 边形没有用到,如 nnn 边形 (3,4,5,6)(3,4,5,6)(3,4,5,6) 和 (3,5,4,6)(3,5,4,6)(3,5,4,6) 是两个相同的 nnn 边形,(3,4,5,6)(3,4,5,6)(3,4,5,6) 和 (4,5,6,7)(4,5,6,7)(4,5,6,7) 是两个不相同的 nnn 边形。
现在有 ttt 和 nnn,表示 ttt 个询问并且询问的是 nnn 边形,每个询问给定一个区间 [l,r][l,r][l,r],问有多少个 nnn 边形(要求该 nnn 边形自己的 nnn 条边的长度互不相同)的值在该区间范围内。
输入格式
第一行包含两个正整数 ttt、nnn,表示有 ttt 个询问,询问的是 nnn 边形。
接下来 ttt 行,每行有两个空格隔开的正整数 lll、rrr,表示询问区间 [l,r][l,r][l,r]。
输出格式
输出共 ttt 行,第 iii 行对应第 iii 个查询的 nnn 边形个数。
样例输入
4 3 1 10 30 50 60 200 200 400
样例输出
0 1 18 32
import os
import sys
input=sys.stdin.readline
#dfs求所有的n边形,边长乘积不超过100000
def dfs(depth,last_val,tot,mul):
#depth 第depth边,last_val指上一条边长,tot即边长之和,mul即边长之积
#递归出口
if depth==n:
#可行性剪枝
#满足n边形基本定义
#最小的n-1边之和大于第n边 tot-path[-1]>path[-1]
if tot>2*path[-1]:
#合法的n边形
ans[mul]+=1
return
#枚举第depth条边的边长为i
for i in range(last_val+1,100000):
#最优化剪枝
#先前选择了depth个数字,乘积为mul
#后续还有n-depth个数字,每个数字都要>i
if mul*(i**(n-depth))<=100000:
path.append(i)
dfs(depth+1,i,tot+i,mul*i)
path.pop()
else:
break
t,n=map(int,input().split())
#ans[i]表示价值为i的n边形
ans=[0]*100001
path=[]
dfs(0,0,0,1)
#每次询问一个区间l,r,输出多少个n边形的价值在[l,r]中
#等价于ans[l]+....+ans[r],需要对ans求前缀和
for i in range(100001):
ans[i]+=ans[i-1]
for _ in range(t):
l,r=map(int,input().split())
print(ans[r]-ans[l-1])
记忆化搜索
混境之地
问题描述
小蓝有一天误入了一个混境之地。
好消息是:他误打误撞拿到了一张地图,并从中获取到以下信息:
- 混境之地是一个 n⋅mn \cdot mn⋅m 大小的矩阵,其中第 iii 行第 jjj 列的的点 hijh_{ij}hij 表示第 iii 行第 jjj 列的高度。
- 他现在所在位置的坐标为 (A,B)(A, B)(A,B) ,而这个混境之地出口的坐标为 (C,D)(C, D)(C,D) ,当站在出口时即表示可以逃离混境之地。
- 小蓝有一个喷气背包,使用时,可以原地升高 kkk 个单位高度。
坏消息是:
- 由于小蓝的体力透支,所以只可以往低于当前高度的方向走。
- 喷漆背包燃料不足,只可以最后使用一次。
小蓝可以往上下左右四个方向行走,不消耗能量。
小蓝想知道他能否逃离这个混境之地,如果可以逃离这里,输入 Yes
,反之输出 No
。
输入格式
第 111 行输入三个正整数 n,mn, mn,m 和 kkk , n,mn, mn,m 表示混境之地的大小, kkk 表示使用一次喷气背包可以升高的高度。
第 222 行输入四个正整数 A,B,C,DA, B, C, DA,B,C,D ,表示小蓝当前所在位置的坐标,以及混境之地出口的坐标。
第 333 行至第 n+2n + 2n+2 行,每行 mmm 个整数,表示混境之地不同位置的高度。
输出格式
输出数据共一行一个字符串:
- 若小蓝可以逃离混境之地,则输出
Yes
。 - 若小蓝无法逃离混境之地,则输出
No
。
样例输入1
5 5 30 1 1 5 5 3 20 13 12 11 19 17 33 72 10 12 23 12 23 9 21 43 23 12 2 21 34 23 12 1
样例输出1
Yes
样例解释1
从 (1,1)(1, 1)(1,1) 到 (5,5)(5, 5)(5,5) 的一条可行道路为:
- 在 (1,1)(1, 1)(1,1) 处使用喷气背包。
- (1,1)−>(1,2)−>(1,3)−>(1,4)−>(1,5)−>(2,5)−>(3,5)−>(4,5)−>(5,5)(1, 1) -> (1, 2) -> (1, 3) -> (1, 4) -> (1, 5) -> (2, 5) -> (3, 5) -> (4, 5) -> (5, 5)(1,1)−>(1,2)−>(1,3)−>(1,4)−>(1,5)−>(2,5)−>(3,5)−>(4,5)−>(5,5) 。
样例输入2
5 5 10 1 1 5 5 3 2 13 12 11 1 17 33 72 10 12 23 12 23 9 21 43 23 12 2 21 34 23 12 1
样例输出2
No
样例解释2
可以证明不存在一条路径可以从起点到达终点。
import sys
input=sys.stdin.readline
def dfs(x,y,z):
#x当前横坐标,y当前纵坐标,z有无使用背包
if x==C and y==D:
return True
for dx,dy in [(0,1),(0,-1),(1,0),(-1,0)]:
xx,yy=dx+x,dy+y
if xx<0 or xx>=n or yy<0 or yy>=m: #边界判断
continue
if a[xx][yy]<a[x][y]: #新位置高度低于当前位置
#直接走
if dfs(xx,yy,z):
return True
if a[xx][yy]-a[x][y]<k and z==False: #新位置高度高于当前位置,且相差小于k,用掉背包
if dfs(xx,yy,True):
return True
return False
n,m,k=map(int,input().split())
A,B,C,D=map(int,input().split())
A,B,C,D=A-1,B-1,C-1,D-1
a=[]
for i in range(n):
a.append(list(map(int,input().split())))
if dfs(A,B,False):
print("Yes")
else:
print("No")