第二周作业(Python版本)
1.带分数
问题描述:
100 可以表示为带分数的形式:100 = 3 + 69258 / 714。
还可以表示为:100 = 82 + 3546 / 197。
注意特征:带分数中,数字1~9分别出现且只出现一次(不包含0)。
类似这样的带分数,100 有 11 种表示法。
输入格式
从标准输入读入一个正整数N (N<1000*1000)
输出格式
程序输出该数字用数码1~9不重复不遗漏地组成带分数表示的全部种数。
注意:不要求输出每个表示,只统计有多少表示法!
样例输入1
100
样例输出1
11
样例输入2
105
样例输出2
6
解题思路:
问题可以转化为n=a+b/c,b=(n-a)*c,用暴力求解就能求解出来。
因为共有9位数字可以用,所以c最大能取到四位数字,a最大能取到n-1。数字1~9分别出现且只出现一次(不包含0),用if语句即可筛选出来。
代码:
# encoding:utf-8
n=input()
#n=a+b/c b=(n-a)*c
count=0
for a in range(0,int(n)):
for c in range(1,9999):
b=(int(n)-int(a))*int(c)
list1=list(str(a))+list(str(b))+list(str(c))
if list1.count('1')==1 and list1.count('2')==1 and list1.count('3')==1 and list1.count('4')==1 and list1.count('5')==1 and list1.count('6')==1 and list1.count('7')==1 and list1.count('8')==1 and list1.count('9')==1 and list1.count('0')==0:
count+=1
print(count)
参考链接: link.
2.李白打酒
问题描述:
话说大诗人李白,一生好饮。幸好他从不开车。
一天,他提着酒壶,从家里出来,酒壶中有酒2斗。他边走边唱:
无事街上走,提壶去打酒。
逢店加一倍,遇花喝一斗。
这一路上,他一共遇到店5次,遇到花10次,已知最后一次遇到的是花,他正好把酒喝光了。
请你计算李白遇到店和花的次序,可以把遇店记为a,遇花记为b。则:babaabbabbabbbb 就是合理的次序。像这样的答案一共有多少呢?请你计算出所有可能方案的个数(包含题目给出的)。
解题思路:
使用递归求解,保证最后一次为花,且酒的数量为1。
代码:
# encoding:utf-8
res = 0
def dajiu(store,flower,wine):
global res
if store>0:
dajiu(store-1,flower,wine*2)
if flower>1:#最后一次为花
dajiu(store,flower-1,wine-1)
if (store==0 and flower==1 and wine==1):
res += 1
return res
print (dajiu(5,10,2))
运行结果:14
参考链接: link.
总结:
3.第39级台阶
问题描述:
小明刚刚看完电影《第39级台阶》,离开电影院的时候,他数了数礼堂前的台阶数,恰好是39级!
站在台阶前,他突然又想着一个问题:
如果我每一步只能迈上1个或2个台阶。先迈左脚,然后左右交替,最后一步是迈右脚,也就是说一共要走偶数步。那么,上完39级台阶,有多少种不同的上法呢?
输出格式:
输出一个整数
解题思路:
使用递归,寻找登上39个台阶且步数为偶数的情况
代码:
# encoding:utf-8
def find(n, s): #n代表阶梯数,s代表步数
if n > 39:
return 0
elif n == 39 and s % 2 == 0:
return 1
return find(n + 1, s + 1) + find(n + 2, s + 1)
print(find(0,0))
运行结果:51167078
参考链接: link.
4.穿越雷区
问题描述:
已知的地图是一个方阵,上面用字母标出了A,B区,其它区都标了正号或负号分别表示正负能量辐射区。
例如:
A + - + -
- + - - +
- + + + -
+ - + - +
B + - + -
坦克车只能水平或垂直方向上移动到相邻的区。
数据格式要求:
输入第一行是一个整数n,表示方阵的大小, 4<=n<100
接下来是n行,每行有n个数据,可能是A,B,+,-中的某一个,中间用空格分开。
A,B都只出现一次。
要求输出一个整数,表示坦克从A区到B区的最少移动步数。
如果没有方案,则输出-1
例如:
用户输入:
5
A + - + -
- + - - +
- + + + -
+ - + - +
B + - + -
则程序应该输出:
10
资源约定:
峰值内存消耗 < 512M
CPU消耗 < 1000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意: main函数需要返回0
注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
注意: 所有依赖的函数必须明确地在源文件中 #include , 不能通过工程。
解题思路:
广度优先搜索
代码:
# encoding:utf-8
#穿越雷区
n = int(input())
m = [input().split(' ') for _ in range(n)]
visit = [[False] * n for _ in range(n)] # 记录是否访问过
step = [(0, -1), (0, 1), (-1, 0), (1, 0)]
queue = [(0, 0, 0)] # 存三个值:坐标、当前移动的步数
while queue:
y, x, t = queue.pop(0)
if m[y][x] == 'B':
print(t)
break
for dy, dx in step:
ny = y + dy
nx = x + dx
if -1 < nx < n and -1 < ny < n:
if not visit[ny][nx] and m[y][x] != m[ny][nx]:
# 没有访问过并且不是连续走相同的区域
queue.append((ny, nx, t+1))
visit[y][x] = True
if not queue:
print(-1)
参考链接:link.
5.迷宫
问题描述:
下图给出了一个迷宫的平面图,其中标记为 1 的为障碍,标记为 0 的为可 以通行的地方。
010000
000100
001001
110000
迷宫的入口为左上角,出口为右下角,在迷宫中,只能从一个位置走到这 个它的上、下、左、右四个方向之一。 对于上面的迷宫,从入口开始,可以按DRRURRDDDR 的顺序通过迷宫, 一共 10 步。其中 D、U、L、R 分别表示向下、向上、向左、向右走。 对于下面这个更复杂的迷宫(30 行 50 列),请找出一种通过迷宫的方式, 其使用的步数最少,在步数最少的前提下,请找出字典序最小的一个作为答案。 请注意在字典序中D<L<R<U。(如果你把以下文字复制到文本文件中,请务 必检查复制的内容是否与文档中的一致。在试题目录下有一个文件 maze.txt, 内容与下面的文本相同)
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一 个字符串,包含四种字母 D、U、L、R,在提交答案时只填写这个字符串,填 写多余的内容将无法得分
解题思路:
这道题目可以采用深度优先搜索的方法,利用队列进行储存当前的位置,如果走到终点的话,队列就不再加入新东西。
代码:
# encoding:utf-8
from queue import Queue
maze = []
with open('maze.txt') as f:
for line in f.readlines():
maze.append(list(line.strip()))
m = len(maze)
n = len(maze[0])
visit = [[True]*n for _ in range(m)]
queue = Queue()
queue.put((0, 0, ''))
while queue:
x, y, path = queue.get()
if x == m - 1 and y == n - 1:# 走到终点结束
print(path)
break
where = ['D', 'U', 'L', 'R']
for i, (x1, y1) in enumerate([(1, 0), (-1, 0), (0, -1), (0, 1)]):
new_x, new_y = x + x1, y+y1
if 0<=new_x<m and 0<=new_y<n and maze[new_x][new_y] == '0' and visit[new_x][new_y]:
queue.put((new_x, new_y, path+ where[i]))
visit[new_x][new_y] = False
参考链接:link.
6.跳马
问题描述:
中国象棋半张棋盘如图1所示。马自左下角(0,0)向右上角(m,n)跳。规定只能往右跳,不准往左跳。比如图1中所示为一种跳行路线,并将路径总数打印出来。
输入格式:
只有一行:两个数n,m
输出格式:
只有一个数:总方案数total。
解题思路:
仔细想想马跳的情况就四种:右一上二,右一下二,右二上一,右二下一,而马的每一个位置都只有这四种跳法,求每一位置马的跳格最终就可跳到目标位置,所以用递归方法求解。
代码:
# encoding:utf-8
totle=0
def jump(r,c,a,b):#r,c表示马现在的位置,a,b分别表示马往右和往上跳几格
global totle
if(r<0 or r>m or c<0 or c>n):
return
if(r+a==m and c+b==n):#判断是否是解
totle += 1
return
#马符合题目要求的四种跳法,在马的当前位置分别选择枚举这四种跳法会将所有情况枚举完
jump(r+a,c+b,2,1)
jump(r+a,c+b,-2,1)
jump(r+a,c+b,1,2)
jump(r+a,c+b,-1,2)
m,n = map(int,input().split())
jump(0,0,0,0)
print("%d"%totle)
参考链接:link.
7.路径之谜
问题描述:
小明冒充X星球的骑士,进入了一个奇怪的城堡。
城堡里边什么都没有,只有方形石头铺成的地面。
假设城堡地面是 n x n 个方格。【如图1.png】所示。
按习俗,骑士要从西北角走到东南角。
可以横向或纵向移动,但不能斜着走,也不能跳跃。
每走到一个新方格,就要向正北方和正西方各射一箭。
(城堡的西墙和北墙内各有 n 个靶子)
同一个方格只允许经过一次。但不必做完所有的方格。
如果只给出靶子上箭的数目,你能推断出骑士的行走路线吗?
有时是可以的,比如图1.png中的例子。
本题的要求就是已知箭靶数字,求骑士的行走路径(测试数据保证路径唯一)
输入:
第一行一个整数N(0<N<20),表示地面有 N x N 个方格
第二行N个整数,空格分开,表示北边的箭靶上的数字(自西向东)
第三行N个整数,空格分开,表示西边的箭靶上的数字(自北向南)
输出:
一行若干个整数,表示骑士路径。
为了方便表示,我们约定每个小格子用一个数字代表,从西北角开始编号: 0,1,2,3…
比如,图1.png中的方块编号为:
0 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15
示例:
用户输入:
4
2 4 3 4
4 3 3 3
程序应该输出:
0 4 5 1 2 3 7 11 10 9 13 14 15
资源约定:
峰值内存消耗 < 256M
CPU消耗 < 1000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意:不要使用package语句。不要使用jdk1.7及以上版本的特性。
注意:主类的名字必须是:Main,否则按无效代码处理。
解题思路:
这道题一看路线问题应该要使用dfs,我们使用反向思维,我们不断的拔剑,把剑拔完或者有一个为零而且到了就可以了。当到达终点时,除了要判断该点的坐标是不是终点坐标,或者判断所有的箭都已经拔完。
代码:
# encoding:utf-8
path = [0 for _ in range(401)]# 记录走过的点
dx = [1, 0, -1, 0]
dy = [0, 1, 0, -1]# 我们的移动
cntx = [0 for _ in range(21)]
cnty = [0 for _ in range(21)]# 记录箭的数量
maps = [[False for _ in range(21)]for _ in range(21)]# 用于判断是否走过
sun = False
tot = 0# 箭的总数
def check():# 判断是否完成,如果有一个为零,就不能往下走了
for i in range(0, n):
if cntx[i] != 0 or cnty[i] != 0:
return False
return True
def dfs(x, y, num):
global sun
path[num] = y *n + x # 坐标点
maps[x][y] = True
cntx[x] -= 1 # 我们这步走过来拔剑
cnty[y] -= 1 # 我们这步走过拔剑
if x == n and y == n and check():
sun = True# 完成走了
return
for i in range(4):
x1 = x + dx[i]
y1 = y + dy[i]
if not sun and not maps[x1][y1] and 0 <= x1 < n and 0 <= y1 < n:
if cntx[x1] > 0 and cnty[y1] > 0:
dfs(x1, y1, num+1)
maps[x1][y1] = False# 回溯,至关重要
cntx[x1] += 1
cnty[y1] += 1
n = int(input())
for i in range(0, n):
cntx[i] = int(input())
tot += cntx[i]
for i in range(0, n):
cnty[i] = int(input())
tot += cnty[i]
dfs(0, 0, 0)
print(path[:tot//2])# 走了箭的一半的数量应该没毛病
参考链接: link.
8.未名湖边的烦恼
问题描述:
问题描述
每年冬天,北大未名湖上都是滑冰的好地方。北大体育组准备了许多冰鞋,可是人太多了,每天下午收工后,常常一双冰鞋都不剩。
每天早上,租鞋窗口都会排起长龙,假设有还鞋的m个,有需要租鞋的n个。现在的问题是,这些人有多少种排法,可以避免出现体育组没有冰鞋可租的尴尬场面。(两个同样需求的人(比如都是租鞋或都是还鞋)交换位置是同一种排法)
输入格式
两个整数,表示m和n
输出格式
一个整数,表示队伍的排法的方案数。
样例输入
3 2
样例输出
5
数据规模和约定
m,n∈[0,18]
解题思路:
假设还鞋的都叫A,借鞋子的都叫B,拿题目给的3个人还,2个人借来说,就是A,B的排列组合,手动排出来如下:
AAABB
AABBA
ABABA
ABAAB
AABAB
一共是这五种。我们知道还鞋的必须大于等于借鞋的,即m>=n,我们假设一个递归函数fun_Rec(m, n),返回的是m人还鞋,n人借鞋 的排列数。一个人,要么借鞋要么还鞋。
这个队伍从后面开始看,甲可能是借鞋子的,也可能是还鞋的,所以
fun_Rec(3, 2)就有两种可能:fun_Rec(3, 1),fun_Rec(2, 2)
所以递归关系来了:
fun_Rec(m, n)=fun_Rec(m, n-1)+fun_Rec(m-1, n)
在m>=n的时候递归
在m<n的时候返回0
在n=0的时候(就是没有人借鞋子的时候,只有一种排法)返回1
代码:
#递归
# encoding:utf-8
def fun_Rec(m,n): #m是还,n是借
if n > m:
return 0
elif n == 0:
return 1
else:
return fun_Rec(m, n-1) + fun_Rec(m-1, n)
s = list(map(int, input().split()))
m, n = s[0], s[1]
print(fun_Rec(m, n))
#动态规划:dp[x][y] = dp[x][y-1] + dp[x-1][y]
# encoding:utf-8
s = list(map(int, input().split()))
m, n = s[0], s[1]
dp = [[0 for i in range(0, n+1)] for j in range(0, m+1)]
for i in range(1, m+1):
dp[i][0] = 1 #把没有人借鞋子的那一列都置为1,因为没人借鞋子的时候只有一种排法
for x in range(1, m+1):
for y in range(1, n+1):
if x >= y:
dp[x][y] = dp[x][y-1] + dp[x-1][y]
else:
continue
print(dp[m][n])
参考链接:link.
总结:
9.大臣的旅费
问题描述:
问题描述
很久以前,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最多花费的路费是多少。
样例输入1
5
1 2 2
1 3 1
2 4 5
2 5 4
样例输出1
135
输出格式
大臣J从城市4到城市5要花费135的路费。
解题思路:
这道题的思考点在于随便找一个点,现在假设找到1这个点,从1这个点出发,找到距离1最远的点x,然后再从x这个点出发,再找到距离x最远的点,这个点就是大臣要走的最远距离。
代码:
# encoding:utf-8
cnt=0
node=0
def dfs(v,k): #进行dfs搜索
global cnt
global node
global vis
if k>cnt:
cnt=k
node=v
for i in range(len(E[v])):
if vis[E[v][i][0]]==False: #没访问过的点都进行一次dfs
vis[E[v][i][0]]=True #进行标记
dfs(E[v][i][0],k+E[v][i][1])
vis[E[v][i][0]]=False #回溯
n=int(input())
E=[[]for i in range(n+1)]
vis=[False for i in range(n+1)]
for i in range(n-1):
x,y,z=map(int,input().split())
E[x].append((y,z))
E[y].append((x,z)) #将各个点进行连接,用列表数组的方式进行
vis[1]=True
dfs(1,0) #找到距离1最远的点node
vis[1]=False
cnt=0
vis[node]=True
dfs(node,0) #从node出发,找到最远的点,就是所要求的最远距离
res=0
for i in range(1,cnt+1):
res+=i+10
print(res)
参考链接: link.
10.2n皇后问题
问题描述:
Description
给定一个n*n的棋盘,棋盘中有一些位置不能放皇后。现在要向棋盘中放入n个黑皇后
和n个白皇后,使任意的两个黑皇后都不在同一行、同一列或同一条对角线上,任意的两
个白皇后都不在同一行、同一列或同一条对角线上。问总共有多少种放法?n小于等于8。
Input
输入的第一行为一个整数n,表示棋盘的大小。
接下来n行,每行n个0或1的整数,如果一个整数为1,表示对应的位置可以放皇后,
如果一个整数为0,表示对应的位置不可以放皇后。
Output
输出一个整数,表示总共有多少种放法。
Sample Input
No.1
4
1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1
No.2
4
1 0 1 1
1 1 1 1
1 1 1 1
1 1 1 1
Sample Output
No.1
2
No.2
0
解题思路:
n皇后问题:用回溯遍历所有情况。
判断下一位置是否合理。
设置递归出口,第n行安全。此时cnt+1。继续回溯遍历找到所有解。
2n皇后问题:
一种皇后放完后怎么保存这种皇后对下一种皇后的影响?
回溯只有要考虑上一种皇后,回溯是不是有点复杂?
因为要求得所有解,所以必须遍历所有情况,所以横向循环,纵向递归,遍历了所有的情况,没有用到回溯。
里面d存储的是前面已经存储好的黑皇后的location,就像n皇后问题一样。然后每一次递归列数从0开始,将这一列的所有情况遍历完,当该行有位置是合理的,就从此处生成一个递归分支,由此就可以遍历所有情况。
代码:
# encoding:utf-8
cnt = 0
def issafe(d, row):
for i in range(row):
if d[i] == d[row] or abs(i - row) == abs(d[i] - d[row]):
return False
return True
def blackDfs(c, d, n, row):
global cnt #已经放置了n个白皇后,放置了n个黑皇后则计数
if row == n:
cnt += 1
return
d[row] = 0
while d[row] < n:
if c[row][d[row]] == 1 and issafe( d, row):
blackDfs(c, d, n, row + 1)
d[row] += 1
def dfs(a, b, n, row):
if row == n:
c = [[0 for i in range(n)] for i in range(n)]
for i in range(n):
for j in range(n):
c[i][j] = a[i][j] # save a to c
for i in range(n):
c[i][b[i]] = 0
d = [0 for i in range(n)]
blackDfs(c, d, n, 0)
return
b[row] = 0
while b[row] < n:
if a[row][b[row]] == 1 and issafe(b, row):
dfs(a, b, n, row + 1)
b[row] += 1
def main():
n = int(input())
a = [[0 for i in range(n)] for i in range(n)]
b = [0 for i in range(n)]
for i in range(n):
tmp = input().split()
tmp=list(map(int,tmp))
a[i]=tmp
dfs(a, b, n, 0)
print(cnt)
if __name__ == '__main__':
main()
参考链接:link.