欢迎交流学习~~
专栏: 蓝桥杯Python组刷题日寄
蓝桥杯进阶系列:
🏆 Python | 蓝桥杯进阶第一卷——字符串
🔎 Python | 蓝桥杯进阶第二卷——贪心
💝 Python | 蓝桥杯进阶第三卷——动态规划
✈️ Python | 蓝桥杯进阶第四卷——图论
🌞 Python | 蓝桥杯进阶第五卷——数论
💎 Python | 蓝桥杯进阶第六卷——搜索
Python|蓝桥杯进阶第六卷——搜索
- 🎁 危险系数
- 🌲 穿越雷区
- 🚀 地宫取宝
- 💡 2n皇后问题
- 💖 学霸的迷宫
🎁 危险系数
题目:
时间限制:
1s
内存限制:
128MB
题目描述:
抗日战争时期,冀中平原的地道战曾发挥重要作用。
地道的多个站点间有通道连接,形成了庞大的网络。但也有隐患,当敌人发现了某个站点后,其它站点间可能因此会失去联系。
我们来定义一个危险系数 DF(x,y)
:
对于两个站点 x
和 y (x != y)
, 如果能找到一个站点 z
,当 z
被敌人破坏后,x
和 y
不连通,那么我们称 z
为关于 x,y
的关键点。相应的,对于任意一对站点 x
和 y
,危险系数 DF(x,y)
就表示为这两点之间的关键点个数。
本题的任务是:已知网络结构,求两站点之间的危险系数。
输入描述:
输入数据第一行包含 2
个整数 n(2 <= n <= 1000), m(0 <= m <= 2000)
,分别代表站点数,通道数;
接下来 m
行,每行两个整数 u,v (1 < = u, v < = n; u != v)
代表一条通道;
最后 1
行,两个数 u,v
,代表询问两点之间的危险系数 DF(u, v)
。
输出描述:
一个整数,如果询问的两点不连通则输出 -1
.
样例输入:
7 6
1 3
2 3
3 4
3 5
4 5
5 6
1 6
样例输出:
2
解题思路
深度优先搜索,对于每条从 u
到 v
的路径,对于路径上经过的每个结点 i
,其到达 v
的路径数 path[i]
都记 +1
,搜索结束后若 path[i]==path[v]
则说明这个点为关键点。
具体思路见参考代码及其注释。
参考代码
# 深度优先搜索
def dfs(u, v):
global path
# 退出条件
if u == v:
# 当访问到最后的站点 end
for i in range(n):
# 遍历所有站点
if visited[i] == True:
# 如果这个站点在这条路线上
path[i] += 1
return
for i in range(n):
if mat[u][i] == 1 and not visited[i]:
# 当 start 和 i 两站点连通
# 且站点 i 未被访问
visited[i] = True
# 递归
dfs(i, v)
# 回溯
visited[i] = False
if __name__ == '__main__':
n, m = map(int, input().split())
# mat 为邻接矩阵
mat = [[0 for j in range(n)] for i in range(n)]
for i in range(m):
a, b = map(int, input().split())
mat[a-1][b-1] = 1
mat[b-1][a-1] = 1
# u, v 两个节点
u, v = map(int, input().split())
u, v = u-1, v-1
# visited[i] 表示站点 i 是否被访问
visited = [False for i in range(n)]
# path[i] 表示经过站点 i 到终点 v 的路径数
path = [0 for i in range(n)]
dfs(u, v)
# path[v]为从 u 到 v 的路径数
# 当其他站点也为这个数时,说明该站点为关键点
print(path.count(path[v]) - 1)
# 这里只需要 -1 是表示 v 结点
# 而不用考虑 u 结点,是因为我们设置邻接矩阵时有 mat[i][i] = 0
🌲 穿越雷区
题目:
时间限制:
1s
内存限制:
128MB
题目描述:
X 星的坦克战车很奇怪,它必须交替地穿越正能量辐射区和负能量辐射区才能保持正常运转,否则将报废。
某坦克需要从 A
区到 B
区去(A
,B
区本身是安全区,没有正能量或负能量特征),怎样走才能路径最短?
已知的地图是一个方阵,上面用字母标出了 A
,B
区,其它区都标了正号或负号分别表示正负能量辐射区。
例如:
A + - + -
- + - - +
- + + + -
+ - + - +
B + - + -
坦克车只能水平或垂直方向上移动到相邻的区。
输入描述:
输入第一行是一个整数 n
,表示方阵的大小, 4<=n<100
接下来是 n
行,每行有 n
个数据,可能是 A
,B
,+
,-
中的某一个,中间用空格分开。
A
,B
都只出现一次。
输出描述:
要求输出一个整数,表示坦克从A区到B区的最少移动步数。
如果没有方案,则输出-1
样例输入:
5
A + - + -
- + - - +
- + + + -
+ - + - +
B + - + -
样例输出:
10
解题思路
本题要利用 广度优先搜索 的知识。
其中用 visiteded
来记录结点是否被访问过。
用 res
来记录每层搜索结点的坐标和到达该结点的最少移动次数。
其搜索结构可以用如下伪代码描述:
while res:
# 退出条件
if 到达目标结点:
break
# 遍历四个搜索方向
for i in range(4):
if 满足所需要求:
将该结点相关信息添加到 res 中
# 该结点搜索完毕,删去信息
res.pop(0)
参考代码
# 广度优先搜索
# 输入数据
n = int(input())
cell = [list(input().split()) for i in range(n)]
# visited 记录该点是否被访问过
visited = [[False for j in range(n)] for i in range(n)]
visited[0][0] = True
# dx dy 表示向上下左右四个方向移动时的坐标变换
dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]
# res 用来记录当前(搜索)层各结点的坐标和到达该结点的最少移动次数
res = [[0, 0, 0]]
while res:
# 终止条件:到达目标结点
if cell[res[0][0]][res[0][1]] == 'B':
print(res[0][2])
break
# 广度优先搜索,遍历四个方向
for i in range(4):
# 新坐标
new_x = res[0][0] + dx[i]
new_y = res[0][1] + dy[i]
# 如果新结点的坐标不超过边界
if (0 <= new_x <= n-1) and (0 <= new_y <= n-1):
# 新结点的坐标坐标没有被访问过
# 并且新旧两个结点的状态不同,即不能是同一个正负状态
if (not visited[new_x][new_y]) and cell[res[0][0]][res[0][1]] != cell[new_x][new_y]:
# 添加该结点到res
res.append([new_x, new_y, res[0][2]+1])
visited[new_x][new_y] = True
# 该结点搜索完毕
res.pop(0)
# 如果最后不能到达 B 结点
# 此时 res 为空
if len(res) == 0:
print('-1')
🚀 地宫取宝
题目:
时间限制:
1s
内存限制:
128MB
题目描述:
X 国王有一个地宫宝库。是
n
×
m
n \times m
n×m 个格子的矩阵。每个格子放一件宝贝。每个宝贝贴着价值标签。
地宫的入口在左上角,出口在右下角。
小明被带到地宫的入口,国王要求他只能向右或向下行走。
走过某个格子时,如果那个格子中的宝贝价值比小明手中任意宝贝价值都大,小明就可以拿起它(当然,也可以不拿)。
当小明走到出口时,如果他手中的宝贝恰好是k件,则这些宝贝就可以送给小明。
请你帮小明算一算,在给定的局面下,他有多少种不同的行动方案能获得这k件宝贝。
输入描述:
输入一行 3
个整数,用空格分开:n
m
k
(1< =n,m< =50, 1< =k< =12)
接下来有 n
行数据,每行有 m
个整数 Ci (0< =Ci< =12)
代表这个格子上的宝物的价值
输出描述:
要求输出一个整数,表示正好取 k
个宝贝的行动方案数。该数字可能很大,输出它对 1000000007
取模的结果。
样例输入:
2 3 2
1 2 3
2 1 5
样例输出:
14
解题思路
深度优先搜索 + 动态规划。
创建 dp
数组,其中 dp[i][j][k][l]
表示坐标为 (i,j)
点物品数量为 k
, 最大价值为 l
的方案数。
具体思路见参考代码和注释。
参考代码
# 深度优先搜索
def dfs(x, y, loads, max_value):
global n, m, k, cell, dp
# 该结点已经访问过
if dp[x][y][loads][max_value+1] != -1:
return dp[x][y][loads][max_value+1]
# 如果到达了终点
if (x == n-1) and (y == m-1):
# 若此时物品数量已经为 k, 即已满
# 或者终点物品价值比最大价值还要大
if (loads == k) or (loads == k-1 and cell[x][y] > max_value):
dp[x][y][loads][max_value+1] = 1
return 1
else:
dp[x][y][loads][max_value+1] = 0
return 0
# count 用来记录方案数
count = 0
# 取出当前位置的物品价值
current_value = cell[x][y]
d = [[0, 1], [1, 0]]
# 遍历右和下两个方向
for i in range(2):
# 移动后的新坐标
new_x = x + d[i][0]
new_y = y + d[i][1]
# 如果新坐标没有超出边界
if (new_x < n) and (new_y < m):
# 如果当前位置的物品可以拿起
if (current_value > max_value) and (loads < k):
# count 要加上如下搜索的方案数:
# 从当前位置开始搜索, 物品数量为 loads+1, 最大价值为 current_value
count = count + dfs(new_x, new_y, loads+1, current_value)
# 还要加上不拿起该位置的物品的方案数
count = count + dfs(new_x, new_y, loads, max_value)
# 赋值
dp[x][y][loads][max_value+1] = count
return dp[x][y][loads][max_value+1]
if __name__ == '__main__':
n, m, k = map(int, input().split())
cell = [list(map(int, input().split())) for i in range(n)]
# dp[i][j][k][l] 表示坐标为 (i,j) 点物品数量为 k, 最大价值为 l 的方案数
dp = [[[[-1 for _ in range(14)] for _ in range(13)] for j in range(m+1)] for i in range(n+1)]
# 输出最后结果
# 从 (0, 0) 开始搜索, 此时物品数量为 0, 最大价值设为负数
print(dfs(0, 0, 0, -1) % 1000000007)
💡 2n皇后问题
题目:
时间限制:
1s
内存限制:
128MB
题目描述:
给定一个 n*n
的棋盘,棋盘中有一些位置不能放皇后。现在要向棋盘中放入 n
个黑皇后和 n
个白皇后,使任意的两个黑皇后都不在同一行、同一列或同一条对角线上,任意的两个白皇后都不在同一行、同一列或同一条对角线上。问总共有多少种放法?n
小于等于 8
。
输入描述:
输入的第一行为一个整数 n
,表示棋盘的大小。 n
小于等于 8
接下来 n
行,每行 n
个 0
或 1
的整数,如果一个整数为 1
,表示对应的位置可以放皇后,如果一个整数为 0
,表示对应的位置不可以放皇后。
输出描述:
输出一个整数,表示总共有多少种放法。
样例输入:
4
1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1
样例输出:
2
解题思路
根据题意我们得到:每行必然有一个黑皇后和一个白皇后。
我们的思路是:先将各行的黑皇后放好,再将各行的白皇后放好。
定义 s
为皇后状态,s=2
为黑皇后,s=3
为白皇后。
即对于 cell
表示各结点状态:有 0
表示不可以放皇后,1
表示可以放皇后,2
表示黑皇后,3
表示白皇后。
定义 check(row, col, s, cell)
函数,用来判断坐标 (row, col)
放 s
对应的皇后是否可行。
定义深度优先搜索函数 dfs(row, n, s, cell)
搜索第 row
行(最多有 n
行)是否可以放 s
对应的皇后。
参考代码
n = int(input())
cell = [list(map(int, input().split())) for i in range(n)]
count = 0
# check 函数,判断皇后放的位置是否满足条件
def check(row, col, s, cell):
# 检查对应列是否有 s
for i in range(row-1, -1, -1):
if cell[i][col] == s:
return False
# 检查左对角线
r = row - 1
c = col - 1
while r >= 0 and c >= 0:
if cell[r][c] == s:
return False
r -= 1
c -= 1
# 检查右对角线
r = row - 1
c = col + 1
while r >= 0 and c < n:
if cell[r][c] == s:
return False
r -= 1
c += 1
return True
# dfs 函数
def dfs(row, n, s, cell):
global count
# 判断是否到最后一行
# 这里是 n 表示,row == n-1 时已经搜索完毕
# 再次递归时有 row == n,这是说明已经到底
if row == n:
# 若为黑皇后
if s == 2:
# 则接下来从第一行开始放白皇后
dfs(0, n, 3, cell)
# 若为白皇后
# 此时黑白皇后已经全部放完
# 退出
if s == 3:
count += 1
return
# 遍历每行
for col in range(n):
# 如果 cell[row][col] 不能再放皇后
if cell[row][col] != 1:
continue
# 如果该结点可以放皇后
if check(row, col, s, cell):
# 改变结点状态
cell[row][col] = s
# 递归搜索下一行
dfs(row+1, n, s, cell)
# 回溯
cell[row][col] = 1
if __name__ == '__main__':
dfs(0, n, 2, cell)
print(count)
💖 学霸的迷宫
题目:
时间限制:
1s
内存限制:
128MB
题目描述:
学霸抢走了大家的作业,班长为了帮同学们找回作业,决定去找学霸决斗。但学霸为了不要别人打扰,住在一个城堡里,城堡外面是一个二维的格子迷宫,要进城堡必须得先通过迷宫。因为班长还有妹子要陪,磨刀不误砍柴功,他为了节约时间,从线人那里搞到了迷宫的地图,准备提前计算最短的路线。可是他现在正向妹子解释这件事情,于是就委托你帮他找一条最短的路线。
输入描述:
第一行两个整数 n
, m
,为迷宫的长宽。
接下来n行,每行 m
个数,数之间没有间隔,为 0
或 1
中的一个。0
表示这个格子可以通过,1
表示不可以。假设你现在已经在迷宫坐标 (1,1)
的地方,即左上角,迷宫的出口在 (n,m)
。每次移动时只能向上下左右 4
个方向移动到另外一个可以通过的格子里,每次移动算一步。数据保证 (1,1)
,(n,m)
可以通过。
输出描述:
第一行一个数为需要的最少步数 K
。
第二行 K
个字符,每个字符 ∈{U,D,L,R}
,分别表示上下左右。如果有多条长度相同的最短路径,选择在此表示方法下字典序最小的一个。
样例输入:
3 3
001
100
110
样例输出:
4
RDRD
解题思路
本题利用广度优先搜索。
注意题目中要求:
如果有多条长度相同的最短路径,选择在此表示方法下字典序最小的一个。
因此我们搜索的顺序是 D, L, R, U
,即:下左右上。
思路与上文中穿越雷区相同。
参考代码
# 广度优先搜索
n, m = map(int, input().split())
cell = [list(map(int, input())) for i in range(n)]
# visited用来记录结点访问状态
visited = [[False for j in range(m)] for i in range(n)]
visited[0][0] = True
# dx, dy 表示 下左右上 方向的坐标变动
d = ['D', 'L', 'R', 'U']
dx = [1, 0, 0, -1]
dy = [0, -1, 1, 0]
# res 用来记录当前搜索层结点的结果
# res[0][0] 表示横坐标
# res[0][1] 表示纵坐标
# res[0][2] 表示到达该结点的移动次数
# res[0][3] 表示到达该结点的路径
res = [[0, 0, 0, []]]
while res:
# 终止条件:到达目标结点
if (res[0][0] == n-1) and (res[0][1] == m-1):
print(res[0][2])
print(''.join(res[0][3]))
# 遍历四个方向
for i in range(4):
new_x = res[0][0] + dx[i]
new_y = res[0][1] + dy[i]
new_path = res[0][3] + [d[i]]
# 如果新坐标不超过边界
if (0 <= new_x <= n-1) and (0 <= new_y <= m-1):
# 如果该结点没有被访问过,且可以通过
if (not visited[new_x][new_y]) and cell[res[0][0]][res[0][1]] == 0:
res.append([new_x, new_y, res[0][2]+1, new_path])
visited[new_x][new_y] = True
# 该结点搜索完毕
res.pop(0)