【思特奇杯·云上蓝桥-算法集训营】第1周

目录

一、跑步训练

题干

解答

二、阶乘约数

题干

解答

三、出栈次序

题干

解答

四、哥德巴赫分解

题干

解答

五、图书排列

题干

解答

六、猴子分香蕉

题干

解答

七、稍小分数

题干

解答

八、excel地址

题干

解答

九、日期问题

题干

解答

十、整数划分

题干

解答

十一、一步之遥

题干

解答

*十二、啦啦队

题干

解答

*十三、七星填空

题干

解答



一、跑步训练

题干

小明要做一个跑步训练,初始时,小明充满体力,体力值计为 10000。

如果小明跑步,每分钟损耗 600 的体力。

如果小明休息,每分钟增加 300 的体力。

体力的损耗和增加都是 均匀变化的。

小明打算跑一分钟、休息一分钟、再跑一分钟、再休息一分钟……如此循环。

如果某个时刻小明的体力到达 0,他就停止锻炼, 请问小明在多久后停止锻炼。

解答

def train(health):
    t=0
    for k in range(1,100):
        if health-300*k>0 and health-300*k<600:  #消耗完k个300后,剩下的体力不够600
            t=(health-300*k)/10
            break
    print(k)
    t=t+120*k
    return t

print(train(10000))

答案:3880

二、阶乘约数

题干

定义阶乘 n! = 1 × 2 × 3 × ··· × n

请问 100! 100 的阶乘)有多少个约数。

解答

原本的思路:

#求1到100,每个数字分别的约数个数,再相加
def count(num):
    sum=1
    for i in range(1,num):
        if num%i == 0:
            sum=sum+1
    return sum

def addUp(total):
    sum=0
    for i in range(1,total+1):
        sum = sum + count(i)
    return sum

print(addUp(100))

后来发现不对,因为有些数(比如4的阶乘和5的阶乘)约数是相同的,不能这么算。

遂找答案,发现一个公式:

任意一个正整数 X 都可以表示成若干个质数乘积的形式,即 X = p1^α1 ∗ p2^α2 …… ∗ pk^αk

约数个数 = (a1 + 1)(a2 + 1)……(ak + 1)   

举例:24可分解为2x2x2x3,p1=2,α1=3,p2=3,α2=1

(插播一句吐槽:话说这个公式有证明过程吗...)

于是这道题就变成了求质数+求每个质数的个数

def judge(n):  #判断是不是素数
    flag = True
    for i in range(2,n):
        if n%i==0:
            flag=False
            break
    return flag

x=[2]
for i in range(3,100):
    if judge(i):
        x.append(i)

a = {}
for i in x:
    a[i] = 0

for i in range(2, 101):
    for j in x:
        while i % j == 0:   #eg:24/2=12,12/2=6,6/2=3,3/3=1
            i = i / j
            a[j] = a[j] + 1
sum = 1
for i in a.values():
    sum *= (i+1)
print(sum)

答案:39001250856960000

三、出栈次序

题干

X星球特别讲究秩序,所有道路都是单行线。

一个甲壳虫车队,共16辆车,按照编号先后发车,夹在其它车流中,缓缓前行。

路边有个死胡同,只能容一辆车通过,是临时的检查站,如图所示。

X星球太死板,要求每辆路过的车必须进入检查站,也可能不检查就放行,也可能仔细检查。

如果车辆进入检查站和离开的次序可以任意交错。

那么,该车队再次上路后,可能的次序有多少种?

为了方便起见,假设检查站可容纳任意数量的汽车。

显然,如果车队只有1辆车,可能次序1种;2辆车可能次序2种;3辆车可能次序5种。

现在足足有16辆车啊,亲!需要你计算出可能次序的数目。

解答

def xStar(n):
    f = {}
    f[0] = 1
    f[1] = 1
    f[2] = 2
    f[3] = 5
    for i in range(4,n+1):
        f[i] = 0

    for i in range(4, n+1):
        for j in range(0, i):
            f[i] = f[i] + f[j] * f[i - 1 - j]
    return f[n]

print(xStar(16))

 答案:35357670

四、哥德巴赫分解

题干

解答

import math
def maxnum(n):   #存储最大值
    max=0
    for i in range(6,n+2,2):
        if find(i)>max:
            max=find(i)
    return max

def find(n):   #找出数字n是哪两个素数之和,返回较小值的最大值
    min=0
    for i in range(3,n):
        if judge(i) and judge(n-i):
            min=i
            break
    return min

def judge(n):  #判断是不是素数
    flag = True
    for i in range(2,n):
        if n%i==0:
            flag=False
            break
    if n==1:
        flag = False
    return flag

print(maxnum(10000))

答案:173 (为7246的分解)

五、图书排列

题干

将编号为1~1010本书排放在书架上,要求编号相邻的书不能放在相邻的位置。

请计算一共有多少种不同的排列方案。

解答

参考了答案:

res = 0
a = [i for i in range(10)]     #生成一个数组,为书的编号
n = [False for _ in range(10)]     #生成一个数组,值全为False,表示书全没放下

def check():
    for i in range(9):
        if abs(a[i]-a[i+1]) == 1:    #如果有两本相邻的书放在一起,则这种情况不成立
            return False
    return True

def dfs(k):
    global res   #全局变量,存储排列的情况
    if k == 10:     #当书的本书为10时
        if check():
            res += 1
            return
    for i in range(10):
        if not n[i]:
            n[i] =True    #某本书放了吗?
            a[k] = i
            dfs(k+1)
            n[i] = False   #重置

dfs(0)
print(res)

六、猴子分香蕉

题干

解答

for i in range(6,10000):
    if (i-1)%5 != 0:
        continue
    m=i
    m=(m-1)/5*4

    if (m-2)%5 != 0:
        continue
    m=(m-2)/5*4

    if (m-3)%5 != 0:
        continue
    m=(m-3)/5*4

    if (m-4)%5 != 0:
        continue
    m=(m-4)/5*4

    if m%5 !=0 or m==0:
        continue
    print(i)                          

答案:3141

七、稍小分数

题干

回到小学----
真分数:分子小于分母的分数
既约分数:分子分母互质,也就是说最大公约数是1

x星球数学城的入口验证方式是:
屏幕上显示一个真分数,需要你快速地找到一个比它小的既约分数,要求这个分数越大越好。
同时限定你的这个分数的分母不能超过100

解答

def find(a,b):   #a为分子,b为分母
    dic={}
    flag=True
    bPlus=0
    for i in range(2,a+1):   #a的约数
        if a%i == 0:
            dic[i]=i

    for i in range(b+1,101):
        for j in range(2,i+1):
            if i%j == 0:
                if j in dic:
                    flag=False
                    break
        if flag == True:
            bPlus=i
            break
        elif flag == False:
            flag= True
            
    if bPlus != 0:
        return bPlus
    elif bPlus == 0:
        return "所求分数的既约分母超过了100,请重新输入"

x=int(input())
y=int(input())

if isinstance(find(x,y),int):
    print("最大的既约分数为:"+str(x)+"/"+str(find(x,y)))
else:
    print(find(x,y))

八、excel地址

题干

Excel单元格的地址表示很有趣,它使用字母来表示列号。

  比如,

  A表示第1列,

  B表示第2列,

  Z表示第26列,

  AA表示第27列,

  AB表示第28列,

  BA表示第53列,

  ....

  当然Excel的最大列号是有限度的,所以转换起来不难。

  如果我们想把这种表示法一般化,可以把很大的数字转换为很长的字母序列呢?

  本题目即是要求对输入的数字, 输出其对应的Excel地址表示方式。

样例输入

26

样例输出

Z

样例输入

2054

样例输出

BZZ

解答

#伪·将x转换为26进制数
def change(x):
    a=[]
    flag=True
    while (flag):
        rest=x%26
        if rest==0:
            rest=26
            x=x-1
        a.append(rest)
        x=x//26
        if x==0:
            flag=False

    a.reverse()
    return a

def acs(*args):    
    a=[]
    for i in range(len(args)):
        b=args[i]
        a.append( chr(int(b)+64) )
    c=''.join(a)
    return c

temp=change( int(input() ) )
print(acs(*temp))

九、日期问题

题干

小明正在整理一批历史文献。这些历史文献中出现了很多日期。小明知道这些日期都在1960年1月1日至2059年12月31日。令小明头疼的是,这些日期采用的格式非常不统一,有采用年/月/日的,有采用月/日/年的,还有采用日/月/年的。更加麻烦的是,年份也都省略了前两位,使得文献上的一个日期,存在很多可能的日期与其对应。

比如02/03/04,可能是2002年03月04日、2004年02月03日或2004年03月02日。

给出一个文献上的日期,你能帮助小明判断有哪些可能的日期对其对应吗?

输入

一个日期,格式是"AA/BB/CC"。 (0 <= A, B, C <= 9)

输出

输出若干个不相同的日期,每个日期一行,格式是"yyyy-MM-dd"。多个日期按从早到晚排列。

样例输入

02/03/04

样例输出

2002-03-04

2004-02-03

2004-03-02

解答

格式杀我

def day(y,m,d):    #判断日期是否合法
    flag = False
    if y%4==0 and m==2 and d<=29:
        flag=True
    if y%4!=0 and m==2 and d<=28:
        flag = True
    if m==1 or m==3 or m==5 or m==7 or m==8 or m==10 or m==12:
        if d<=31:
            flag=True
    if m==4 or m==6 or m==9 or m==11:
        if d<=30:
            flag=True
    return flag

s=input()
a=int(s[0:2])
b=int(s[3:5])
c=int(s[6:8])

if b<=12 and day(a,b,c):     #年月日的情况
    if a>=60:
        print("{}-{:0>2}-{:0>2}".format(str(a+1900), str(b), str(c)))
    elif a<60:
        print("{}-{:0>2}-{:0>2}".format(str(a+2000), str(b), str(c)))

if a<=12 and day(c,a,b):     #月日年的情况
    if c>=60:
        print("{}-{:0>2}-{:0>2}".format(str(c+1900), str(a), str(b)))
    elif c<60:
        print("{}-{:0>2}-{:0>2}".format(str(c+2000), str(a), str(b)))

if b<=12 and day(c,b,a):     #日月年的情况
    if c>=60:
        print("{}-{:0>2}-{:0>2}".format(str(c+1900), str(b), str(a)))
    elif c<60:
        print("{}-{:0>2}-{:0>2}".format(str(c+2000), str(b), str(a)))

十、整数划分

题干

对于一个正整数n的划分,就是把n变成一系列正整数之和的表达式。注意,分划与顺序无关,例如6=5+1.跟6=1+5是同一种分划,另外,这个整数本身也是一种分划。

例如:对于正整数n=5,可以划分为:

1+1+1+1+1

1+1+1+2

1+1+3

1+2+2

2+3

1+4

5

输入描述

输入一个正整数n

输出描述

输出n整数划分的总数k

输入样例

5

输出样例

7

解答

def divide(n,m):     #把n分为不超过m个数和的划分
    if n==1 or m==1:
        return 1
    if n==m:
        return divide(n,m-1)+1    #把划分为自己的情况刨去,求不超过m-1种划分的情况数量
    if n>m:
        return divide(n,m-1)+divide(n-m,m)
    if n<m:
        return divide(n,n)        #划分数不能超过自己本身

num=int(input())
print(divide(num,num))

十一、一步之遥

题干

从昏迷中醒来,小明发现自己被关在X星球的废矿车里。

矿车停在平直的废弃的轨道上。

他的面前是两个按钮,分别写着“F”和“B”。

小明突然记起来,这两个按钮可以控制矿车在轨道上前进和后退。

按F,会前进97米。按B会后退127米。

透过昏暗的灯光,小明看到自己前方1米远正好有个监控探头。

他必须设法使得矿车正好停在摄像头的下方,才有机会争取同伴的援助。

或许,通过多次操作F和B可以办到。

矿车上的动力已经不太足,黄色的警示灯在默默闪烁...

每次进行 F 或 B 操作都会消耗一定的能量。

小明飞快地计算,至少要多少次操作,才能把矿车准确地停在前方1米远的地方。

请填写为了达成目标,最少需要操作的次数。

解答

for y in range(1,100):
    if (127*y+1) % 97 == 0:
        x=(127*y+1)//97
        print(x,y)          #x=55,y=42  
        break             

答案:97

*十二、啦啦队

题干

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的人数,保证人数合理性。

要求输出一个整数,表示可以产生的花样种数。

例如:

用户输入:

1 2

程序应该输出:

3

再例如:

用户输入:

3 3

程序应该输出:

4

解答

用了一种非常不严谨的解答方式。

思路:当金字塔底部队列确定时,上面的也就决定了。

第一步:枚举底部的所有可能

第二步:依次验证这种排列方式是否可行(验证A、B个数是否刚好用完)

第三步:把所有可能的情况进行计数累加

后两步都简单,但第一步又是排列组合,排不清楚了...所以我采取了随机生成底部列表的方式。理论上说,只要随机次数足够多,底部所有的情况都能被枚举出来

import random
import copy

def judge(a,b,*args):  #判断以某一种摆法为底时,这种摆法可不可行
    flag=False
    numa=a
    numb=b
    c=[]
    c.append(args)
    for i in range(1,len(args)):   #第i层
        d=[]
        for j in range(0,len(args)-i):
            if (c[i-1][j]==0 and c[i-1][j+1]==0) or (c[i-1][j]==1 and c[i-1][j+1]==1):
                d.append(1)
                numa=numa-1
            else:
                d.append(0)
                numb=numb-1
        c.append(d)
    if numa==0 and numb==0:
        flag=True
    return flag

def count(x,y,bottom):
    sum=0
    dic={}
    for i in range(0,1000):   #可适当修改随机的次数
        numa=0
        numb=0
        a = []
        b = []
        for j in range(0, bottom):   #生成随机数
            b = random.randint(0, 1)
            a.append(b)
        for j in range(0,len(a)):    #统计该底A,B的个数
            if a[j]==1:
                numa=numa+1
            numb=bottom-numa
        if judge(x-numa,y-numb,*a) == True:    #传入剩余个数
            if a not in dic.values():
                sum=sum+1
                b=copy.deepcopy(a)
                dic[i]=b
        a.clear()

    return sum

a=int(input())
b=int(input())
sum=a+b    #总共有多少人

for i in range(sum):    #最底层长度
    sum=sum-i
    if sum==0:
        bottom=i
        break

print(count(a,b,bottom))

*十三、七星填空

题干

如下图所示。在七角星的 14 个节点上填入 1 ~ 14的数字,不重复,不遗漏。 要求每条直线上的四个数字之和必须相等。

解答

由于每个元素都被用了两次,所以各边相加等于30。总之就是解一个含五个参数的方程...我感觉是可行的但是溢出太多程序已经跑不动了...

此刻我脑子已经和糨糊一样了看看过两天能不能想到新思路

import copy
def judge(*args):  #判断所得数组里是否有重复数据
    flag=True
    for i in range(len(args)):
        if flag == False:
            break
        for j in range (i+1,len(args)):
            if args[i]==args[j]:
                flag=False
                break
    return flag

a=[6,0,0,0,0,0,0,14,11,0,0,0,0,0]
flag=True
while(flag):   #解方程
    b = copy.deepcopy(a)
    for x in range(1, 14):
        b[2] = 10 - x
        b[5] = x
        for y in range(1, 14):
            b[3] = y
            b[6] = 13 - y
            for z in range(1, 14):
                b[1] = z
                b[4] = 20 + x - y - z
                for t in range(1, 14):
                    b[9] = t
                    b[12] = 30 - z - x - t
                    for m in range(1, 14):
                        b[10] = m
                        b[11] = -11 + z + x + t - m
                        b[13] = -3 - x + 2 * y + z - m
                        if judge(*b):
                            flag = False  # 若找到不重复的一组数据,则退出循环
                            c = copy.deepcopy(b)
    b.clear()

print(c)

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值