思特奇杯·云上蓝桥 -算法 集训营第二周
1. 带分数
题目描述
100 可以表示为带分数的形式:100 = 3 + 69258 / 714。
还可以表示为:100 = 82 + 3546 / 197。
注意特征:带分数中,数字 1~9 分别出现且只出现一次(不包含 0)。
类似这样的带分数,100 有 11 种表示法。
解法一
解题思路
使用全排列将数组[1,2,3,4,5,6,7,8,9]的所有排列可能求出
在将每一种排列分成三部分a,b,c
a+b/c == n
python代码
#带分数
import itertools
def getnum(l,start,end):
num = 0
for i in range(start,end):
num = num*10 + l[i]
return num
if __name__ == '__main__':
l = [1,2,3,4,5,6,7,8,9]
ans = 0
n = 100
for i in itertools.permutations(l):
for j in range(1,7):
a = getnum(i,0,j)
for k in range(j,len(i)-1):
b = getnum(i,j,k+1)
c = getnum(i,k+1,len(i))
if a + b/c == n:
ans += 1
print(ans)
运行结果
2. 李白打酒
题目描述
话说大诗人李白,一生好饮。幸好他从不开车。
一天,他提着酒壶,从家里出来,酒壶中有酒 2 斗。他边走边唱:
无事街上走,提壶去打酒。
逢店加一倍,遇花喝一斗。
这一路上,他一共遇到店 5 次,遇到花 10 次,已知最后一次遇到的是花,他正
好把酒喝光了。
请你计算李白遇到店和花的次序,可以把遇店记为 a,遇花记为 b。则:
babaabbabbabbbb 就是合理的次序。像这样的答案一共有多少呢?请你计算出
所有可能方案的个数(包含题目给出的)。
解法一
解题思路
用深度优先搜索遍历出所有可能次序
终止条件 店为0,花剩1,酒剩一斗
python代码
#李白打酒,答案14
def solution(d,f,j):
global ans
if d > 0:
solution(d-1,f,j*2)
if f > 1:
solution(d,f-1,j-1)
if d == 0 and f == 1 and j == 1:
ans += 1
if __name__ == '__main__':
ans = 0
solution(5,10,2)
print(ans)
运行结果
3. 第 39 级台阶
题目描述
小明刚刚看完电影《第 39 级台阶》,离开电影院的时候,他数了数礼堂前的
台阶数,恰好是 39 级!
站在台阶前,他突然又想着一个问题:
如果我每一步只能迈上 1 个或 2 个台阶。先迈左脚,然后左右交替,最后一
步是迈右脚,也就是说一共要走偶数步。那么,上完 39 级台阶,有多少种不同
的上法呢?
解法一
深度优先搜索
python代码
'''第39 级台阶
答案51167078
'''
def dfs(n,cnt,step):
global ans
if cnt > n or step > n:
return
elif cnt == 39 and step % 2 == 0:
ans += 1
return
else:
dfs(n,cnt+1,step+1)
dfs(n,cnt+2,step+1)
if __name__ == '__main__':
n = 39
ans = 0
dfs(n,0,0)
print(ans)
解法二
解题思路
动态规划
定义dp1,dp2,dp1[i]表示到第i+1阶台阶刚好是奇数步有dp1[i]种走法,dp2[i]表示到第i+1阶台阶刚好是偶数步有dp2[i]种走法
可知 dp1[0] = 1,dp1[1] = 1,dp2[0] = 0,dp2[1] = 1
递推公式为
dp1[i] = dp2[i - 1] + dp2[i - 2]
dp2[i] = dp1[i - 1] + dp1[i - 2]
python代码
'''第39 级台阶
答案51167078
'''
def solution(n):
dp1 = [0]*39 #存储奇数步
dp2 = [0]*39 #存储偶数步
dp1[0] = 1
dp1[1] = 1
dp2[0] = 0
dp2[1] = 1
for i in range(2,n):
dp1[i] = dp2[i - 1] + dp2[i - 2]
dp2[i] = dp1[i - 1] + dp1[i - 2]
return dp2[-1]
if __name__ == '__main__':
n = 39
ans = solution(n)
print(ans)
运行结果
4. 穿越雷区
题目描述
已知的地图是一个方阵,上面用字母标出了 A,B 区,其它区都标了正号或负号
分别表示正负能量辐射区。
例如:
A + - + -
- + - - +
- + + + -
+ - + - +
B + - + -
坦克车只能水平或垂直方向上移动到相邻的区。
数据格式要求:
输入第一行是一个整数 瀁,表示方阵的大小, 4<=瀁<100
接下来是 瀁 行,每行有 瀁 个数据,可能是 A,B,+,-中的某一个,中间用空格
分开。
A,B 都只出现一次。
要求输出一个整数,表示坦克从 A 区到 B 区的最少移动步数。
如果没有方案,则输出-1
例如:
用户输入:
5
A + - + -
- + - - +
- + + + -
+ - + - +
B + - + -
则程序应该输出:
10
解法一
解题思路
广度优先搜索
python代码
#穿越雷区
if __name__ == '__main__':
n = 5
l = [['A','+','-','+','-'],['-','+','-','-','+'],['-','+','+','+','-'],['+','-','+','-','+'],['B','+','-','+','-']]
visited = [[False]*5 for _ in range(5)]
ax = 0
ay = 0
bx = 4
by = 0
mVis = [[False for _ in range(n)] for _ in range(n) ]
mVis[0][0] = True
#模拟在图中上下左右走
xMove = [-1,1,0,0]
yMove = [0,0,1,-1]
#队列记录坐标和走到的次数
queue = [[0,0,0]]
while queue:
#当走到了b点退出广搜、打印走的次数
if mMap[queue[0][0]][queue[0][1]] == 'B':
print(queue[0][2])
break
for i in range(4):
dx = queue[0][0] + xMove[i]
dy = queue[0][1] + yMove[i]
if dx>=0 and dx<=n-1 and dy>=0 and dy<=n-1:
if not mVis[dx][dy] and mMap[queue[0][0]][queue[0][1]] != mMap[dx][dy]:
'''没有访问过并且不是连续的走-和+就可以将点加入队列'''
queue.append([dx,dy,queue[0][2]+1])
mVis[dx][dy] = True
queue.pop(0)
if len(queue) == 0:
"""队列为空说明走不到B点"""
print("-1")
运行结果
5. 迷宫
题目描述
下图给出了一个迷宫的平面图,其中标记为 1 的为障碍,标记为 0 的为可 以
通行的地方。
010000
000100
001001
110000
迷宫的入口为左上角,出口为右下角,在迷宫中,只能从一个位置走到这 个它
的上、下、左、右四个方向之一。 对于上面的迷宫,从入口开始,可以按
DRRURRDDDR 的顺序通过迷宫, 一共 10 步。其中 D、U、L、R 分别表示向
下、向上、向左、向右走。 对于下面这个更复杂的迷宫(30 行 50 列),请找
出一种通过迷宫的方式, 其使用的步数最少,在步数最少的前提下,请找出字
典序最小的一个作为答案。 请注意在字典序中 D<L<R<U。(如果你把以下文字
复制到文本文件中,请务 必检查复制的内容是否与文档中的一致。在试题目录
下有一个文件 瀀aze.txt, 内容与下面的文本相同)
解法一
解题思路
很明显,这是一个求最短路径的题目。这里用到的方法是 广度优先索搜。
由于在字典序中D<L<R<U,因此如果在有很多方向可以走的情况下,优先考虑往下走,走不通了在往左走,依次类推。
python代码
#迷宫,答案DDDDRRURRRRRRDRRRRDDDLDDRDDDDDDDDDDDDRDDRRRURRUURRDDDDRDRRRRRRDRRURRDDDRRRRUURUUUUUUULULLUUUURRRRUULLLUUUULLUUULUURRURRURURRRDDRRRRRDDRRDDLLLDDRRDDRDDLDDDLLDDLLLDLDDDLDDRRRRRRRRRDDDDDDRR
data = '''01010101001011001001010110010110100100001000101010
00001000100000101010010000100000001001100110100101
01111011010010001000001101001011100011000000010000
01000000001010100011010000101000001010101011001011
00011111000000101000010010100010100000101100000000
11001000110101000010101100011010011010101011110111
00011011010101001001001010000001000101001110000000
10100000101000100110101010111110011000010000111010
00111000001010100001100010000001000101001100001001
11000110100001110010001001010101010101010001101000
00010000100100000101001010101110100010101010000101
11100100101001001000010000010101010100100100010100
00000010000000101011001111010001100000101010100011
10101010011100001000011000010110011110110100001000
10101010100001101010100101000010100000111011101001
10000000101100010000101100101101001011100000000100
10101001000000010100100001000100000100011110101001
00101001010101101001010100011010101101110000110101
11001010000100001100000010100101000001000111000010
00001000110000110101101000000100101001001000011101
10100101000101000000001110110010110101101010100001
00101000010000110101010000100010001001000100010101
10100001000110010001000010101001010101011111010010
00000100101000000110010100101001000001000000000010
11010000001001110111001001000011101001011011101000
00000110100010001000100000001000011101000000110011
10101000101000100010001111100010101001010000001000
10000010100101001010110000000100101010001011101000
00111100001000010000000110111000000001000000001011
10000001100111010111010001000110111010101101111000'''
data_array = []
data_array.append([1]*52)
for i in data.split():
data_array.append([])
data_array[-1].append(1)
for j in i:
data_array[-1].append(int(j))
data_array[-1].append(1)
data_array.append([1]*52)
visited = [] # 用来存放已经走过的点
queue = [[(1, 1)]]
start = (1, 1)
end = (30, 50)
visited.append(start)
def bfs(lst, queue, end):
"""
广度优先搜索
:param lst: 数据
:param queue: 队列
:param end: 终点坐标
:return:
"""
if end in queue[-1]:
return queue
alist = []
for now in queue[-1]:
row, col = now
if lst[row+1][col] == 0 and ((row+1, col) not in visited):
alist.append((row+1, col))
visited.append((row+1, col))
if lst[row][col+1] == 0 and ((row, col+1) not in visited):
alist.append((row, col+1))
visited.append((row, col+1))
if lst[row-1][col] == 0 and ((row-1, col) not in visited):
alist.append((row-1, col))
visited.append((row-1, col))
if lst[row][col-1] == 0 and ((row, col-1) not in visited):
alist.append((row, col-1))
visited.append((row, col-1))
queue.append(alist)
return bfs(lst, queue, end)
queue = bfs(data_array, queue, end)
Stack = []
Stack.append(start)
visited_1 = [start]
while Stack[-1] != end:
now = Stack[-1]
row, col = now
i = queue[len((Stack))]
"""由于是D<L<R<U,所以尝试先走D,在走L,其次是R,U"""
if (row+1, col) in i and (row+1, col) not in visited_1:
Stack.append((row+1, col))
visited_1.append((row+1, col))
continue
elif (row, col-1) in i and (row, col-1) not in visited_1:
Stack.append((row, col-1))
visited_1.append((row, col-1))
continue
elif (row, col+1) in i and (row, col+1) not in visited_1:
Stack.append((row, col+1))
visited_1.append((row, col+1))
continue
elif (row-1, col) in i and (row-1, col) not in visited_1:
Stack.append((row-1, col))
visited_1.append((row-1, col))
continue
else:
"""如果走不下去了,就返回"""
Stack.pop()
length_1 = len(Stack)
strstep = ""
for i in range(1,length_1):
if Stack[i][0] > Stack[i-1][0]:
strstep += "D"
elif Stack[i][0] < Stack[i-1][0]:
strstep += "U"
elif Stack[i][1] > Stack[i-1][1]:
strstep += "R"
else:
strstep += "L"
print(strstep)
运行结果
6. 跳马
题目描述
中国象棋半张棋盘如图 1 所示。马自左下角(0,0)向右上角(m,n)跳。规定只能
往右跳,不准往左跳。比如图 1 中所示为一种跳行路线,并将路径总数打印出来。
解法一
解题思路
每一次跳马都有四种情况,将每一种情况下,x,y的变化记录在数组dx,dy
dx = [1,2,2,1]
dy = [2,1,-1,-2]
然后再用深度优先搜索遍历所有可能求解
python代码
#跳马
def dfs(x,y):
global ans
if x == m and y == n:
ans += 1
return
for i in range(4):
tmp_x = x + dx[i]
tmp_y = y + dy[i]
if tmp_x>=0 and tmp_x<=m and tmp_y>=0 and tmp_y<=n:
dfs(tmp_x,tmp_y)
if __name__ == '__main__':
dx = [1,2,2,1] #跳马x轴上的所有变化增量
dy = [2,1,-1,-2] ##跳马y轴上的所有变化增量
ans = 0
n,m = map(int,input().split())
dfs(0,0)
print(ans)
运行结果
7. 路径之谜
8. 未名湖边的烦恼
题目描述
每年冬天,北大未名湖上都是滑冰的好地方。北大体育组准备了许多冰鞋,
可是人太多了,每天下午收工后,常常一双冰鞋都不剩。
每天早上,租鞋窗口都会排起长龙,假设有还鞋的 瀀 个,有需要租鞋的 瀁
个。现在的问题是,这些人有多少种排法,可以避免出现体育组没有冰鞋可租的
尴尬场面。(两个同样需求的人(比如都是租鞋或都是还鞋)交换位置是同一种
排法)
解法一
解题思路
动态规划
设f(n,m)表示有n个人借鞋,m个 人还鞋时有f(n,m)种排列次序
而有n个人借鞋,m个 人还鞋时可表示为:有n-1个人借鞋,m个 人还鞋+有n个人借鞋,m-1个 人还鞋即
f(n,m) = f(n-1,m) + f(n,m-1)
定义数组a[n][m],其中a[i][j]表示有i个人借鞋,j个 人还鞋时有a[i][j]种排法
当没有人借鞋时,只要还鞋人数不为0都只有1种排法,即a[0][j] = 1 (1<=j<=m)
当没有人还鞋时,只有0种排法,即a[i][0] = 0 (0<=j<=m)
当借鞋人数多于还鞋人数时,有0种排法,即a[i][j] = 0 (i>j)
当借鞋人数少于还鞋人数时,a[i][j] = a[i-1][j] + a[i][j-1] (i<=j)
python代码
#未名湖边的烦恼
def solution(m,n):
a = [[0]*(m+1) for _ in range(n+1)]#生成一个n行m列的数组初始值全为0
for i in range(m+1): #初始化第一行,即当解鞋人数为0时只有1种排列可能
a[0][i] = 1
for i in range(n+1): #初始化第一行,即当换鞋人数为0时只有0种排列可能
a[i][0] = 0
print(a)
for i in range(1,n+1):
for j in range(1,m+1):
if i > j:
a[i][j] = 0
else:
a[i][j] = a[i-1][j] + a[i][j-1]
print(a)
return a[n][m]
if __name__ == '__main__':
m = 3
n = 2
ans = solution(m,n)
print(ans)
运行结果
9. 大臣的旅费
10. 2n 皇后问题
题目描述
给定一个 n*n 的棋盘,棋盘中有一些位置不能放皇后。现在要向棋盘中放入 n 个
黑皇后
和 n 个白皇后,使任意的两个黑皇后都不在同一行、同一列或同一条对角线上,
任意的两
个白皇后都不在同一行、同一列或同一条对角线上。问总共有多少种放法?n小
于等于 8。
解法一
解题思路
我们可以先放好黑皇后再放白皇后,0表示不能放皇后,1表示可以放皇后,2表示放黑皇后,3表示放白皇后。我们每放一个皇后时先检查他所在列,和两边的对角线有没有放皇后或者说是不能放皇后,判断条件是格子的数是否为一,不为一则是放了皇后或者是不能放皇后。
python代码
#2n皇后问题
def dfs(row,n,s,mapL):
global ans
if row == n:#判断是否放完了 最后一行
if s == 2:#黑皇后放完开始放白黄后,2代表黑皇后 ,3代表白皇后
dfs(0,n,3,mapL)
if s == 3:#全部 放完
ans += 1
return
for i in range(n):
if mapL[row][i] != 1:#不为1、说明放了皇后,或者不能皇后
continue
if check(row,i,s,mapL):
mapL[row][i] = s#可以放,将格子的数字变为放置对应皇后的数字-
dfs(row+1,n,s,mapL)
mapL[row][i] = 1#回溯
def check(row,j,s,mapL):
r = row - 1
k = j - 1
for i in range(row-1,-1,-1): #检查对应列
if mapL[i][j] == s:
return False
while r>=0 and k>=0: #检查对应左上角
if mapL[r][k] == s:
return False
r -= 1
k -= 1
r = row -1
k = j + 1
while r>=0 and k<n: #检查对应右上角
if mapL[r][k] == s:
return False
r -= 1
k += 1
return True
if __name__ == '__main__':
n = int(input())
mapL = [list(map(int,input().split())) for _ in range(n)] #模拟棋盘
'''mapL = [[1,1,1,1],
[1,1,1,1],
[1,1,1,1],
[1,1,1,1]
]'''
ans = 0
dfs(0,n,2,mapL)
print(ans)
运行结果