Bootstrap

蓝桥杯DFS合集

本文章集合了个人认为的DFS中一些经典的题目并给出题解与思想,用于学习过程中复习。众所周知,DFS是数据结构中较为基础的搜索算法,当刷题量足够大时,就能对递归、回溯、剪枝有较好的理解,初期不理解的话可以在草稿纸上模拟。

1.树的直径问题(基于模板题大臣的旅费)

 题目描述:

很久以前,T 王国空前繁荣。为了更好地管理国家,王国修建了大量的快速路,用于连接首都和王国内的各大城市。

为节省经费,T 国的大臣们经过思考,制定了一套优秀的修建方案,使得任何一个大城市都能从首都直接或者通过其他大城市间接到达。同时,如果不重复经过大城市,从首都到达每个大城市的方案都是唯一的。

J 是 T 国重要大臣,他巡查于各大城市之间,体察民情。所以,从一个城市马不停蹄地到另一个城市成了 J 最常做的事情。他有一个钱袋,用于存放往来城市间的路费。

聪明的 J 发现,如果不在某个城市停下来修整,在连续行进过程中,他所花的路费与他已走过的距离有关,在走第 x 千米到第 x +1 千米这一千米中( x 是整数),他花费的路费是 x +10 这么多。也就是说走 1 千米花费 11,走 2 千米要花费 23。

J 大臣想知道:他从某一个城市出发,中间不休息,到达另一个城市,所有可能花费的路费中最多是多少呢?

 

输入描述:

输入的第一行包含一个整数 n,表示包括首都在内的 T 王国的城市数。

城市从 1 开始依次编号,1 号城市为首都。

接下来 n -1 行,描述 T 国的高速路( T 国的高速路一定是 n -1 条)。

每行三个整数 Pi​,Qi​,Di​,表示城市 Pi​ 和城市 Qi​ 之间有一条高速路,长度为 Di​ 千米。

输出描述:

输出一个整数,表示大臣 J 最多花费的路费是多少。

 这题就不多赘述,有一个视频讲的很详细,参考了博主py小郑的文章,题解链接见树的直径讲解

 大概的思想就是从任意一个点找到一个最远距离的点,这个点就是树直径的一个端点,从端点出发则可以找到另一个端点并给出树的直径。

AC_code

def dfs(x):
    vis[x] = 1
    for i in range(1,n+1):
        if not vis[i] and mp[x][i] != 0:
            ans[i] = ans[x] + mp[x][i]
            dfs(i)
def costfun(d):
    return int(d * 10 + d*(d+1) / 2)
n = int(input())
mp = [[0]*(n+1) for _ in range(n+1)]
for i in range(n-1):
    x,y,d = map(int,input().split())
    mp[x][y] = d
    mp[y][x] = d
vis = [0] * (n+1)
ans = [0] * (n+1)
dfs(1)
q = ans.index(max(ans))
vis = [0] * (n+1)
ans = [0] * (n+1)
dfs(q)
print(costfun(max(ans)))

2.移动字母

题目描述:移动字母(蓝桥OJ)

 这道题的思想就简单很多,化繁为简,将移动字母替换为移动空格(这道题很类似跳蚱蜢(第八届省赛)那道题,有兴趣的朋友可以去蓝桥OJ上看看).

然后重要的一点就是用字典去存储所有可能的情况。

这道题还用到一个,坐标如何在一维数组以及二维数组间转换的方法(大家完全可以在草稿纸上模拟一下,这个很简单很容易看懂)

AC_code

def dfs(s):
    k1 = list(s)
    space_index = s.index('*')
    x,y = space_index // 3,space_index % 3
    for i in dir:
        nx,ny = x+i[0],y+i[1]
        if nx in [0,1] and ny in [0,1,2]:
            old = k1.copy()
            old[nx * 3 + ny],old[space_index] = old[space_index],old[nx*3+ny]
            if ''.join(old) not in ans.keys():
                ans[''.join(old)] = ans[s] + 1
                dfs(''.join(old))
n = int(input())
ans = dict()
dir = [(-1,0),(1,0),(0,1),(0,-1)]
s = 'ABCDE*'
ans[s] = 0
dfs(s)
for i in range(n):
    s = input()
    if s in ans.keys():print(1)
    else: print(0)

3.连通性判断(基于全球变暖问题)

题目描述:全球变暖(蓝桥OJ)

 这道题用到一个连通性判断,判断一个连通的块是否存在。拿全球变暖这道题来说,如若在搜索的过程中发现某个点上下左右四个方向皆为陆地,就可以判断这个点无法被淹没。于是我们搜索的任务就变成了,寻找有多少个岛屿无法被淹没,这样就又变成了经典的DFS。

AC_code

def dfs(x,y):
    vis[x][y] = 1
    global ans
    if mp[x+1][y] == '#' and  mp[x-1][y] == '#' and  mp[x][y+1] == '#' and mp[x][y-1] == '#':
        ans = 1
    for i in dir:
        nx,ny = x + i[0],y + i[1]
        if 0<=nx<n and 0<=ny<n:
            if not vis[nx][ny] and mp[nx][ny] == '#':
                vis[nx][ny] = 1
                dfs(nx,ny)
n = int(input())
mp = []*(n+1)
for i in range(n):
    mp.append(input())
# print(mp)
dir = [(-1,0),(1,0),(0,1),(0,-1)]
rns = 0
vis = [[0] * (n + 1) for _ in range(n + 1)]
for i in range(n):
    for j in range(n):
        if mp[i][j] == '#' and not vis[i][j]:
            ans = 0
            dfs(i,j)
            if not ans: rns += 1
print(rns)

4.分考场(DFS变形)

题目描述:分考场

 这道题我一开始就想到了并查集,但有些无法下手,但从另一个角度来看,将每个人都给予一个房间,并在搜索的过程中检查一下是否符合条件,或许也能成为一种思路。

AC_code

maxn = 1005
ans = 10000000
a = [[0 for _ in range(maxn)] for _ in range(maxn)]# 用于储存关系,1就是关联
c = [0 for _ in range(maxn)]# 表示第i个屋子里面的人数
f = [[0 for _ in range(maxn)] for _ in range(maxn)]# 表示第i个房间的第j个人的编号

def dfs(x, tot):# x表示是当前判断的第i个同学,tot是表示用掉的屋子数
    global ans
    if tot >= ans:# 进行剪枝
        return
    if x == n+1:# 如果已经搜完了n个人,进行判断
        ans = min(ans, tot)
        return
    for i in range(1, tot+1):# 对已有的屋子进行遍历
        lens = c[i]
        k = 0
        for j in range(1, lens+1):
            if not a[x][f[i][j]]:# 如果这两个人没有关系,k+1
                k += 1
        if k == lens:# 如果屋子里的人都没有与x相关,把x放进屋子里面
            c[i] += 1
            f[i][c[i]] = x
            dfs(x+1, tot)
            c[i] -= 1#回溯
    c[tot+1] += 1
    f[tot+1][c[tot+1]] = x
    dfs(x+1, tot+1)# 回溯
    c[tot+1] -= 1

n = int(input())
m = int(input())
for i in range(m):
    x, y = input().split(' ')
    x = int(x)
    y = int(y)
    a[x][y] = 1
    a[y][x] = 1
dfs(1, 0)
print(ans)

分考场的代码参考了别的博主的代码。

5.排列序数

题目描述:排列序数

这题的思想很简单,就是一个用dfs书写排列的模板。然而dfs大概有两种书写排列的模板,一种是在dfs过程中对数组进行交换,但无法得到字典序,一种就是用vis[i]数组记录dfs的过程(可参考之前一篇文章数字游戏)。那么这道题则是采用第二种方法,得到的数组采用字典存储。

def dfs(s,t):
    global rns
    if s == t:
        tmp = ''.join(b[0:t])
        ans[tmp] = rns
        rns += 1
    else:
        for i in range(t):
            if not vis[i]:
                vis[i] = 1
                b[s] = a[i]
                dfs(s+1,t)
                vis[i] = 0
a = ['a','b','c','d','e','f','g','h','i','j']
vis,b = [0]*10,[0]*10
str1 = input()
ans = dict()
rns = 0
dfs(0,len(str1))
print(ans[str1])
;