一.Sine之舞
最近FJ为他的奶牛们开设了数学分析课,FJ知道若要学好这门课,必须有一个好的三角函数基本功。所以他准备和奶牛们做一个“Sine之舞”的游戏,寓教于乐,提高奶牛们的计算能力。
不妨设
An=sin(1–sin(2+sin(3–sin(4+...sin(n))...)
Sn=(...(A1+n)A2+n-1)A3+...+2)An+1
FJ想让奶牛们计算Sn的值,请你帮助FJ打印出Sn的完整表达式,以方便奶牛们做题。
思路:简单的递归函数,要注意断点处的值
def ann(n,s):
if n%2==1:
ans='sin('+str(n)+'-'+s+')'
else:
ans = 'sin(' + str(n) + '+' + s + ')'
if n==1:
return ans
else:
return ann(n-1,ans)
def an(n,s):
s='sin('+str(n)+')'
if n==1:
return s
return ann(n-1,s)
def sn(n,s):
global N
if n==N:
return s+an(n,'')+'+'+str(1)
else:
s='('+s+an(n,'')+'+'+str(N-n+1)+')'
return sn(n+1,s)
N=eval(input())
print(sn(1,''))
二.矩形面积交
平面上有两个矩形,它们的边平行于直角坐标系的X轴或Y轴。对于每个矩形,我们给出它的一对相对顶点的坐标,请你编程算出两个矩形的交的面积。
思路:先分别计算出长和宽相交的长度,再相乘计算出面积,注意要考虑重合时的多种情况
a=list(map(eval,input().strip().split()))
b=list(map(eval,input().strip().split()))
def cha(c,d,e,f):
#判断相交关系
if e>=c and e<=d:
return min(d,f)-e
if f>=c and f<=d:
return f-max(c,e)
#判断包含关系
if e<=c and f>=d:
return d-c
if c<=e and d>=f:
return f-e
return 0
print("{:.2f}".format(cha(min(a[0],a[2]),max(a[0],a[2]),min(b[0],b[2]),max(b[0],b[2]))*cha(min(a[1],a[3]),max(a[1],a[3]),min(b[1],b[3]),max(b[1],b[3]))))
三.报时助手
给定当前的时间,请用英文的读法将它读出来。
时间用时h和分m表示,在英文的读法中,读一个时间的方法是:
如果m为0,则将时读出来,然后加上“o'clock”,如3:00读作“three o'clock”。
如果m不为0,则将时读出来,然后将分读出来,如5:30读作“five thirty”。
时和分的读法使用的是英文数字的读法,其中0~20读作:
0:zero, 1: one, 2:two, 3:three, 4:four, 5:five, 6:six, 7:seven, 8:eight, 9:nine, 10:ten, 11:eleven, 12:twelve, 13:thirteen, 14:fourteen, 15:fifteen, 16:sixteen, 17:seventeen, 18:eighteen, 19:nineteen, 20:twenty。
30读作thirty,40读作forty,50读作fifty。
对于大于20小于60的数字,首先读整十的数,然后再加上个位数。如31首先读30再加1的读法,读作“thirty one”。
按上面的规则21:54读作“twenty one fifty four”,9:07读作“nine seven”,0:15读作“zero fifteen”。
注意:数字前面有0用int()而不是eval()
num={0:'zero', 1:'one', 2:'two', 3:'three', 4:'four', 5:'five', 6:'six', 7:'seven', 8:'eight', 9:'nine', 10:'ten', 11:'eleven', 12:'twelve', 13:'thirteen', 14:'fourteen', 15:'fifteen', 16:'sixteen', 17:'seventeen', 18:'eighteen', 19:'nineteen', 20:'twenty'}
a,b=map(int,input().strip().split())
if a<=20:
print(num[a],end=' ')
else:
print(num[20],num[a%20],end=' ')
if b==0:
print("o'clock")
elif b<=20:
print(num[b])
else:
n=[0,0,'twenty','thirty','forty','fifty']
if b%10==0:
print(n[b//10])
else:
print(n[b // 10], num[b % 10])
四.数的读法
Tom教授正在给研究生讲授一门关于基因的课程,有一件事情让他颇为头疼:一条染色体上有成千上万个碱基对,它们从0开始编号,到几百万,几千万,甚至上亿。
比如说,在对学生讲解第1234567009号位置上的碱基时,光看着数字是很难准确的念出来的。
所以,他迫切地需要一个系统,然后当他输入12 3456 7009时,会给出相应的念法:
十二亿三千四百五十六万七千零九
用汉语拼音表示为
shi er yi san qian si bai wu shi liu wan qi qian ling jiu
这样他只需要照着念就可以了。
你的任务是帮他设计这样一个系统:给定一个阿拉伯数字串,你帮他按照中文读写的规范转为汉语拼音字串,相邻的两个音节用一个空格符格开。
注意必须严格按照规范,比如说“10010”读作“yi wan ling yi shi”而不是“yi wan ling shi”,“100000”读作“shi wan”而不是“yi shi wan”,“2000”读作“er qian”而不是“liang qian”。
思路:先将数字划分为每四位一组:"亿","万",然后每组进行读数,之后再考虑特殊的读"yi"的情况,注意考虑每组之间读'ling'的问题.
def readnum(n):
ans=''
global num_list
if n//1000>0:
ans+=str(num_list[n//1000])+' qian '
n%=1000
if n//100>0:
ans+=str(num_list[n//100])+' bai '
n %= 100
if n // 10 > 0:
ans +=str(num_list[n // 10]) + ' shi '
if n % 10 > 0:
ans +=str(num_list[n % 10])
else:
if n % 10 > 0:
ans += 'ling ' + str(num_list[n % 10])
else:
n%=100
if n//10>0:
ans+='ling '+str(num_list[n//10])+' shi '
if n%10>0:
ans+=str(num_list[n%10])
else:
if n%10>0:
ans+='ling '+str(num_list[n%10])
else:
if n//100>0:
ans+=str(num_list[n//100])+' bai '
n %= 100
if n // 10 > 0:
ans +=str(num_list[n // 10]) + ' shi '
if n % 10 > 0:
ans +=str(num_list[n % 10])
else:
if n % 10 > 0:
ans += ' ling ' + str(num_list[n % 10])
else:
n%=100
if n//10>0:
if n//10==1:
ans+='shi '
else:
ans+=str(num_list[n//10])+' shi '
if n % 10 > 0:
ans += str(num_list[n % 10])
else:
if n%10>0:
ans+=str(num_list[n%10])
return ans.strip()
num=eval(input().strip())
num_list=['ling','yi','er','san','si','wu','liu','qi','ba','jiu','shi']
ans=''
if num//100000000>0:
a=num//100000000
ans+=readnum(a)+' yi '
num%=100000000
if num//10000>0:
if len(ans)>1 and ans[-2]=='i' and num//10000000==0:
ans+='ling '
a=num//10000
ans+=readnum(a)+' wan '
num%=10000
if num>0:
if len(ans)>1 and ans[-2] in ['i','n'] and num//1000==0:
ans+='ling '
ans+=readnum(num)
print(ans.strip())
五.数组替换
编写并测试如下函数:
void Add (int a[], int m, int b[], int n);
该函数将数组b的前n个元素追加到数组a的前m个元素后,假定数组a具有至少存放m+n个元素的空间。例如,如果数组a为 {22,33,44,55,66,77,88,99},数组b为{20,30,40,50,60,70,80,90},则调用Add(a,5,b,3) 后,将把数组a变为{22,33,44,55,66,20,30,40}。注意数组b并没有改变,而且数组a中只需改变n个元素。
注意:如果a数组还有剩余的空间的话,a数组剩余的元素也要输出
nm=input()
a=list(map(eval,input().strip().split()))
b=list(map(eval,input().strip().split()))
m,n=map(eval,input().split())
ans=[]
for i in range(m):
ans.append(a[i])
for i in range(n):
ans.append(b[i])
if len(ans)<len(a):
for i in range(len(ans),len(a)):
ans.append(a[i])
for i in range(len(ans)-1):
print(ans[i],end=',')
print(ans[len(ans)-1])
六.分苹果
小朋友排成一排,老师给他们分苹果。
小朋友从左到右标号1..N。有M个老师,每次第i个老师会给第Li个到第Ri个,一共Ri-Li+1个小朋友每人发Ci个苹果。
最后老师想知道每个小朋友有多少苹果。
思路:在一个区间上将数做加减法时利用差分数组,直接简化为在端点进行加减法.
from collections import defaultdict
N,M=map(eval,input().split())
d=defaultdict(int)
for i in range(M):
l,r,c=map(int,input().split())
d[l]+=c
d[r+1]-=c
ans=0
for i in range(1,N+1):
ans+=d[i]
print(ans,end=' ')
七.产生数
给出一个整数 n 和 k 个变换规则。规则:一位数可变换成另一个一位数:规则的右部不能为零。
例如:n=234。有规则(k=2):
2-> 5
3-> 6
上面的整数 234 经过变换后可能产生出的整数为(包括原数):
234
534
264
564
共 4 种不同的产生数
问题:给出一个整数 n 和 k 个规则。求出:经过任意次的变换(0次或多次),能产生出多少个不同整数。仅要求输出个数。
思路:构建一个10*10的有向图,代表每种数字可以转换的数字,之后用Floyd算法求出连通块,再根据输入的数字算出答案
n,k=map(int,input().split())
m=[[float('inf') for i in range(10)]for j in range(10)]
for i in range(10):
m[i][i]=1
for i in range(k):
a,b=map(int,input().split())
m[a][b]=1
for k in range(10):
for i in range(10):
for j in range(10):
m[i][j]=min(m[i][j],m[i][k]+m[k][j])
c=[0 for i in range(10)]
for i in range(10):
for j in range(10):
if m[i][j]<float('inf'):
c[i]+=1
ans=1
while n:
ans*=c[n%10]
n//=10
print(ans)
八.传染病控制
研究表明,这种传染病的传播具有两种很特殊的性质;
第一是它的传播途径是树型的,一个人X只可能被某个特定的人Y感染,只要Y不得病,或者是XY之间的传播途径被切断,则X就不会得病。
第二是,这种疾病的传播有周期性,在一个疾病传播周期之内,传染病将只会感染一代患者,而不会再传播给下一代。
这些性质大大减轻了蓬莱国疾病防控的压力,并且他们已经得到了国内部分易感人群的潜在传播途径图(一棵树)。但是,麻烦还没有结束。由于蓬莱国疾控中 心人手不够,同时也缺乏强大的技术,以致他们在一个疾病传播周期内,只能设法切断一条传播途径,而没有被控制的传播途径就会引起更多的易感人群被感染(也 就是与当前已经被感染的人有传播途径相连,且连接途径没有被切断的人群)。当不可能有健康人被感染时,疾病就中止传播。所以,蓬莱国疾控中心要制定出一个 切断传播途径的顺序,以使尽量少的人被感染。你的程序要针对给定的树,找出合适的切断顺序。
思路:先用链表保存输入的连接关系,之后再将他们构造成一棵树,这样可以减少复杂度,再使用dfs寻找最少的感染人数
n,p=map(int,input().split())
linklist=[[] for i in range(n+1)]
for i in range(p):
a,b=map(int,input().split())
linklist[a].append(b)
linklist[b].append(a)
tree=[[] for i in range(n+1)]
def createTree(num):
global tree,linklist
if len(linklist[num])==0:
return
else:
tree[num]=linklist[num][:]
for i in linklist[num]:
linklist[i].remove(num)
createTree(i)
return
createTree(1)
ans=300
def dfs(curFloor,curans):
global ans,tree
nextFloor=[]
minans=300
for i in curFloor:
nextFloor+=tree[i]
if nextFloor==[]:
if curans<ans:
ans=curans
return 0
elif len(nextFloor)-1+curans<=ans:
for i,x in enumerate(nextFloor):
del nextFloor[i]
nextans=dfs(nextFloor,curans+len(nextFloor)-1)
if minans>nextans:
minans=nextans
nextFloor.insert(i,x)
return len(nextFloor)-1+minans
else:
return 300
print(dfs([1],1)+1)
九.士兵排队问题
有N个士兵(1≤N≤26),编号依次为 A,B,C,…,队列训练时,指挥官要把一些士兵从高到矮一次排成一行,但现在指挥官不能直接获得每个人的身高信息,只能获得“P1比P2高”这样的比较 结果(P1、P2∈A,B,C,…,Z,记为 P1> P2),如”A> B”表示A比B高。
请编一程序,根据所得到的比较结果求出一种符合条件的排队方案。
(注:比较结果中没有涉及的士兵不参加排队)
思路:拓扑排序问题
num=set()
linklist=[[] for i in range(26)]
while True:
try:
a,b=input().split('>')
num.add(ord(a)-ord('A'))
num.add(ord(b)-ord('A'))
linklist[ord(a)-ord('A')].append(ord(b)-ord('A'))
except:
break
GT=[[0 for i in range(26)]for j in range(26)]
for i in range(26):
for j in linklist[i]:
GT[j][i]=1
ans=''
while len(num):
flag=True
for i in range(26):
if i in num and sum(GT[i])==0:
ans+=chr(i+ord('A'))
num.remove(i)
for j in linklist[i]:
GT[j][i]=0
flag=False
if flag:
ans='No Answer!'
break
print(ans)
十.素数求和
输入一个自然数n,求小于等于n的素数之和
思路:素数筛模板
n=int(input())
ai=[0 for i in range(n+1)]
for i in range(2,int(n**0.5)+2):
if ai[i]==0:
for j in range(i*i,n+1,i):
ai[j]=1
ans=0
for i in range(2,n+1):
if ai[i]==0:
ans+=i
print(ans)
十一.进制转换
程序提示用户输入三个字符,每个字符取值范围是0-9,A-F。然后程序会把这三个字符转化为相应的十六进制整数,并分别以十六进制,十进制,八进制输出。
s=input().lstrip('0')
s=s.strip()
ans=[]
if len(s)==0:
s='0'
ans.append(s)
d=dict()
d['A']=10
d['B']=11
d['C']=12
d['D']=13
d['E']=14
d['F']=15
f10=0
for i in s:
try:
f10=f10*16+int(i)
except:
f10=f10*16+d[i]
ans.append(str(f10))
f8=''
while f10:
f8+=str(f10%8)
f10//=8
if len(f8)==0:
f8='0'
ans.append(f8[::-1])
s=' '.join(ans)
print(s)
十二.欧拉函数
给定一个大于1,不超过2000000的正整数n,输出欧拉函数,phi(n)的值。
如果你并不了解欧拉函数,那么请参阅提示。
欧拉函数phi(n)是数论中非常重要的一个函数,其表示1到n-1之间,与n互质的数的个数。显然的,我们可以通过定义直接计算phi(n)。
当然,phi(n)还有这么一种计算方法。
首先我们对n进行质因数分解,不妨设n=p1^a1 * p2^a2 * ... * pk^ak (这里a^b表示a的b次幂,p1到pk为k个互不相同的质数,a1到ak均为正整数),那么
phi(n)=n(1-(1/p1))(1-(1/p2))....(1-(1/pk))
稍稍化简一下就是
phi(n)=n(p1-1)(p2-1)...(pk-1)/(p1*p2*...*pk)
计算的时候小心中间计算结果超过int类型上界,可通过调整公式各项的计算顺序避免(比如先做除法)!
n=eval(input())
def find_su(num):
ans=[]
for i in range(2,num+1):#因数可以是大于其平方根的,不重复
if num%i==0:
ans.append(i)
while num%i==0:
num//=i
return ans
def phi(num):
su=find_su(num)
ans=num
for i in su:
ans=ans*(i-1)/i
return int(ans)
print(phi(n))
十三.黑白无常
某寝室的同学们在学术完之后准备玩一个游戏: 游戏是这样的,每个人头上都被贴了一张白色或者黑色的纸,现在每个人都会说一句话“我看到x张白色纸条和y张黑色的纸条”,又已知每个头上贴着白色纸的人 说的是真话、每个头上贴着黑色纸的人说的是谎话,现在要求你判断哪些人头上贴着的是白色的纸条,如果无解输出“NoSolution.”;如果有多组解, 则把每个答案中贴白条的人的编号按照大小排列后组成一个数(比如第一个人和第三个人头上贴着的是白纸条,那么这个数就是13;如果第6、7、8个人都贴的 是白纸条,那么这个数就是678)输出最小的那个数(如果全部都是黑纸条也满足情况的话,那么输出0)
思路:二进制的位运算,
def numPrint(num):#将二进制数打印出来
if num==0:
print(0)
return
temp=0
while num:
temp+=1
if num&1:
print(temp,end='')
num>>=1
def CalLen(num):#计算出二进制数中1的个数
ans=0
while num:
if num&1:
ans+=1
num>>=1
return ans
n=int(input().strip())
nums=[list(map(int,input().strip().split())) for i in range(n)]
N=1<<n#计算出总的状态数量
ansFlag=False
for i in range(N):
flag=-2#定义初始值为-2,因为-1防止与无人说实话+1后==0冲突
length=CalLen(i)
for j in range(n):
temp=1<<j
if temp&i:
if flag==-2:
flag=nums[j][0]
else:
if flag!=nums[j][0]:
flag=-3
break
else:
if nums[j][0]==length:
flag=-3#定义为-3防止与无人说实话的-2冲突,这是绝对不正确的情况
break
if flag==-2:#如果没有人说实话
flag+=1
if flag+1==length:#如果说实话的人的数量等于白色的数量
ansFlag=True
numPrint(i)
break
if not ansFlag:
print("NoSolution.")
十四.盾神与砝码称重
有一天,他在宿舍里无意中发现了一个天平!这 个天平很奇怪,有n个完好的砝码,但是没有游码。盾神为他的发现兴奋不已!于是他准备去称一称自己的东西。他准备好了m种物品去称。神奇的是,盾神一早就 知道这m种物品的重量,他现在是想看看这个天平能不能称出这些物品出来。但是盾神稍微想了1秒钟以后就觉得这个问题太无聊了,于是就丢给了你。
思路:dfs,对于每一块砝码有三种情况:不用,和物品放一边,放物品对面的一边,可以利用这个来进行深度搜索,如果砝码的总重量都没有物品重则称不出来
n,m=map(eval,input().split())
fama=sorted(list(map(eval,input().strip().split())))
goods=list(map(eval,input().strip().split()))
def dfs(num,flag):
if num==fama[flag]:
return True
if flag<n-1:
if dfs(num-fama[flag],flag+1) or dfs(num+fama[flag],flag+1) or dfs(num,flag+1):
return True
return False
for _ in range(m):
igoods=goods[_]
if igoods>sum(fama):
print("NO")
continue
elif igoods==sum(fama):
print("YES")
continue
else:
if dfs(igoods,0):
print("YES")
else:
print("NO")
十五.阶乘
一个整数n的阶乘可以写成n!,它表示从1到n这n个整数的乘积。阶乘的增长速度非常快,例如,13!就已经比较大了,已经无法存放在一个整型变量 中;而35!就更大了,它已经无法存放在一个浮点型变量中。因此,当n比较大时,去计算n!是非常困难的。幸运的是,在本题中,我们的任务不是去计算 n!,而是去计算n!最右边的那个非0的数字是多少。例如,5! = 1*2*3*4*5 = 120,因此5!最右边的那个非0的数字是2。再如:7! = 5040,因此7!最右边的那个非0的数字是4。请编写一个程序,输入一个整数n(n< =100),然后输出n! 最右边的那个非0的数字是多少。
思路:保留三位数字,再对数字相乘,如果末尾为0则舍弃,保留的位数不一定为3位,看输入数据的范围,保留位数越多答案越准确
n=int(input())
ans=1
for i in range(1,n+1):
ans*=i
while ans%10==0:
ans//=10
ans%=1000 #为什么是1000?
print(ans%10)
十六.机器人塔
X星球的机器人表演拉拉队有两种服装,A和B。
他们这次表演的是搭机器人塔。
类似:
A
B B
A B A
A A B B
B B B A B
A B A B B A
队内的组塔规则是:
A 只能站在 AA 或 BB 的肩上。
B 只能站在 AB 或 BA 的肩上。
你的任务是帮助拉拉队计算一下,在给定A与B的人数时,可以组成多少种花样的塔。
输入一行两个整数 M 和 N,空格分开(0<M,N<500),分别表示A、B的人数,保证人数合理性。
要求输出一个整数,表示可以产生的花样种数yi
思路:如果i+1行确定那么i行也确定,由此递推,只需要确定最后一行就可以,所以用位运算遍历状态,保留符合状态的结果,可以利用异或计算
def Check(G):
global N,n
TempG=[[0 for ii in range(n)] for i in range(n)]
for i in range(n):
TempG[n-1][i]=G[i]
for i in range(n-2,-1,-1):
for j in range(i+1):
TempG[i][j]=TempG[i+1][j]^TempG[i+1][j+1]
if sum(map(sum,TempG))==N:#对二维列表求和
return True
return False
M,N=map(int,input().strip().split())
#若下一行确定则上一行也确定
#首先计算出最底层的个数,枚举最底层的排列,若符合要求则答案加一
#(1+n)*n/2=M+N -------> n^2+n-2M-2N=0 ------>n=(-1+(1+8M+8N)**0.5)/2
n=int((-1+(1+8*M+8*N)**0.5)//2)
print(n)
#A为0 B为1
ans=0
for i in range(1<<n):
G=[0 for _ in range(n)]
for j in range(n):
if 1<<j & i:
G[j]=1
if Check(G):
ans+=1
print(ans)
十七.传球游戏
游戏规则是这样的:n个同学站成一个圆圈,其中的一个同学手里拿着一个球,当老师吹哨子时开始传球,每个同学可以把球传给自己左右的两个同学中的一个(左右任意),当老师再次吹哨子时,传球停止,此时,拿着球没传出去的那个同学就是败者,要给大家表演一个节目。
聪明的小蛮提出一个有趣的问题:有多少种不同的传球方法可以使得从小蛮手里开始传的球,传了m次以后,又回到小蛮手里。两种传球的方法被视作不同的方 法,当且仅当这两种方法中,接到球的同学按接球顺序组成的序列是不同的。比如有3个同学1号、2号、3号,并假设小蛮为1号,球传了3次回到小蛮手里的方 式有1-> 2-> 3-> 1和1-> 3-> 2-> 1,共2种。
首先看我最先写的dfs算法,结果毫无疑问超时:
n,m=map(int,input().strip().split())
ans=0
def dfs(times,val):
global n,m,ans
if min(n-val,val)>m-times:
return
if times==m and val==0:
ans+=1
return
dfs(times+1,(val-1+n)%n)
dfs(times+1,(val+1)%n)
dfs(0,0)
print(ans)
改进后的动态规划算法:
n,m=map(int,input().strip().split())
G=[[0 for i in range(n)]for j in range(m)]
G[0][1]=1
G[0][n-1]=1
for i in range(1,m):
for j in range(n):
G[i][j]=G[i-1][(j+n-1)%n]+G[i-1][(j+1)%n]
print(G[m-1][0])
十八.机器人繁殖
X星系的机器人可以自动复制自己。它们用1年的时间可以复制出2个自己,然后就失去复制能力。
每年X星系都会选出1个新出生的机器人发往太空。也就是说,如果X星系原有机器人5个,
1年后总数是:5 + 9 = 14
2年后总数是:5 + 9 + 17 = 31
如果已经探测经过n年后的机器人总数s,你能算出最初有多少机器人吗?
思路:找规律
n,s=map(int,input().strip().split())
x1=(1<<(n+1))-1
x2=0
for i in range(1,n+1):
x2+=(1<<i)-1 #记得加括号
print(int((s+x2)//x1))