DFS python

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≤10N10,表示棋盘和皇后的数量

输出描述

为一个正整数,表示对应输入行的皇后的不同放置数量。

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 边形。

现在有 tttnnn,表示 ttt 个询问并且询问的是 nnn 边形,每个询问给定一个区间 [l,r][l,r][l,r],问有多少个 nnn 边形(要求该 nnn 边形自己的 nnn 条边的长度互不相同)的值在该区间范围内。

输入格式

第一行包含两个正整数 tttnnn,表示有 ttt 个询问,询问的是 nnn 边形。

接下来 ttt 行,每行有两个空格隔开的正整数 lllrrr,表示询问区间 [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])

记忆化搜索

混境之地

问题描述

小蓝有一天误入了一个混境之地。

好消息是:他误打误撞拿到了一张地图,并从中获取到以下信息:

  1. 混境之地是一个 n⋅mn \cdot mnm 大小的矩阵,其中第 iii 行第 jjj 列的的点 hijh_{ij}hij 表示第 iii 行第 jjj 列的高度。
  2. 他现在所在位置的坐标为 (A,B)(A, B)(A,B) ,而这个混境之地出口的坐标为 (C,D)(C, D)(C,D) ,当站在出口时即表示可以逃离混境之地。
  3. 小蓝有一个喷气背包,使用时,可以原地升高 kkk 个单位高度。

坏消息是:

  1. 由于小蓝的体力透支,所以只可以往低于当前高度的方向走。
  2. 喷漆背包燃料不足,只可以最后使用一次。

小蓝可以往上下左右四个方向行走,不消耗能量。

小蓝想知道他能否逃离这个混境之地,如果可以逃离这里,输入 Yes ,反之输出 No

输入格式

111 行输入三个正整数 n,mn, mn,mkkkn,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) 处使用喷气背包。
  2. (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")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值