2019【算法】【字节跳动】【笔试】

一. 母牛生小牛

题目:

母牛从3~7岁初每年会生产1头小母牛,10岁后死亡(10岁任然存活),假设初始有一头刚出生的母牛,请问第n年有多少头母牛?(年从第一年开始计数)

注:第三年初会出生 第一头母牛,故第三年有两头母牛。

第五年初,第三年出生的母牛会生产,故第五年有五头母牛。

岁数是虚数

示例:

输入 输出
2 1
3 2
4 3
5 5
12 123

分析:

为计算第n年的牛数,我们需要知道每头牛的年龄。年龄不需要存储和更新,只记录每年有多少头小牛出生,即可推算得到。

  • 创建列表,记录每年牛的总数和当年出生的小牛数[all_nums, birth_num];
  • 第n年新生小牛数 = 处在生育年龄段内小牛牛数总和
  • 第n年的牛数 = 10年内出生的小牛总和

代码:

import numpy as np
# 输入要计算牛数的年份
n = int(input())
#n = 12
#创建列表,记录每年牛的总数和当年出生的小牛数[all_nums, birth_num];
nums = []
# 初始化第1年小牛数:共1头牛,新出生1头牛
nums.append([1, 1])
nums = np.array(nums)

# 计算往后每年的牛数
for year in range(1, n):
    #第n年新生小牛数 = 处在生育年龄段内小牛牛数总和
    if year >= 7:
        birth_num = sum(nums[-6:-1, 1])   #[:][-6:-1])
    else:
        birth_num = sum(nums[:-1, 1])
        
    #第n年的牛数 = 10年内出生的小牛总和
    if year >= 10:
        all_nums = sum(nums[-9:, 1]) + birth_num
    else:
        all_nums = sum(nums[:, 1]) + birth_num
    new = np.array([all_nums, birth_num])
    nums = np.row_stack((nums, new))
print(nums[-1, 0])
#print(nums)

二、雀魂麻将

题目:

小包最近迷上了一款叫做雀魂的麻将游戏,但这个游戏规则太复杂,小包玩了几个月了还是输多赢少。

于是生气的小包根据游戏简化了一下规则发明了一种新的麻将,只留下一种花色,并且除去了一些特殊和牌方式(例如七对子等)。规则如下:

1. 共有36张牌,每张牌是1~9。每个数字4张牌。

2. 你手里有其中的14张牌,如果这14张牌满足如下条件,即算作和牌:

  • 14张牌中有2张相同数字的牌,称为雀头。
  • 除去上述2张牌,剩下12张牌可以组成4个顺子或刻子。顺子为递增的连续3个数字牌(如234,567等),刻子为3个相同的数字牌(如:111,777)

1 1 1 2 2 2 6 6 6 7 7 7 9 9 可以组成1,2,6,7的4个刻子和9的雀头,可以和牌

1 1 1 1 2 2 3 3 5 6 7 7 8 9 用1做雀头,组成123,123,567,789的四个顺子,可以和牌;

1 1 1 2 2 2 3 3 3 5 6 7 7 9 无论用1 2 3 7哪个做雀头,都无法和牌

现在,小包从36张牌中抽取了13张牌,他想知道在剩下的23张中取一张牌,取到哪几种数字可以和牌。

输入描述:输入只有一行,包含13个数组,用空格分隔,每个数字在1~9之间,保证同种数字最多出现4次。

输出描述:输出同样是一行,包含1个或以上的数字。代表他再次取到哪种数字可以和牌。若满足条件的有多种,请按从小到大的顺序输出。若无法和牌,请输出一个数字0。

分析:

最多有9种抽牌方式能让小包和牌,判断每种抽牌方式是否和牌即可。问题转化为判断14张牌是否和牌。

判断是否和牌时,当有顺子或刻子,取出这些牌,然后判断剩下的牌是不是能组成顺子或刻子。递归问题。

代码:

a = input().split()
alist = [int(x) for x in a]

#alist = [1, 1, 1, 2, 2, 2, 6, 6, 6, 7, 7, 7, 9]

He_kinds = []

# 判断是否和牌
def isHe(cardlist):
    l = len(cardlist)
    if l == 0:
        return True
    count0 = cardlist.count(cardlist[0])
    
    # 没出现过雀头,且第一个数字出现的次数 >= 2,且去掉雀头剩下的能组成和牌
    if l % 3 != 0 and count0 >= 2 and isHe(cardlist[2:]) == True:
        return True
    # 如果第一个数字出现次数 >= 3,且去掉这个刻子后剩下的能和牌
    if count0 >= 3 and isHe(cardlist[3:]) == True:                         
        return True
    # 如果存在顺子,且移除顺子后剩下的能和牌
    if cardlist[0]+1 in cardlist and cardlist[0]+2 in cardlist:
        c_cardlist = cardlist[1:]
        c_cardlist.remove(cardlist[0]+1)
        c_cardlist.remove(cardlist[0]+2)
        if isHe(c_cardlist) == True:
            return True
    # 以上条件都不满足,则不能和牌
    return False
    
# 最多有9种抽牌方法可以和牌,计算每一种能不能和牌
for i in range(1, 10):
    if isHe(sorted(alist + [i])) == True:       # 如果这种抽牌方式可以和牌
        He_kinds.append(i)                      # 加入和牌类型列表

print(len(He_kinds))            # 输入和牌方式有多少种
#print(He_kinds)

三、埋伏方案

题目:

我叫王大锤,是一名特工。我刚刚接到任务:在字节跳动大街进行埋伏,抓捕恐怖分子孔连顺。和我一起行动的还有另外两名特工。

1. 我们在字节跳动大街的N个建筑中选定3个埋伏地点。

2. 为了相互照应,我们决定相距最远的两名特工间的距离不超过D。

我特喵是个天才!经过精密的计算,我们从x中可行的埋伏方案中选出一种,这个方案万无一失,颤抖吧,孔连顺!

……

万万没想到,计划还是失败了,孔连顺化妆成小龙女,混在人群中逃出了字节跳动大街。只怪他的伪装太成功了。

请听题:给定N(可选作为埋伏点的建筑物数)、D(距离最远的两名特工的最大值)以及可选建筑物的坐标,计算在这次行动中,共有多少种埋伏选择。

注意:

1.两个特工不能埋伏在同一地点

2.三个特工是等价的:即同样的位置组合(A、B、C)只算一种方案,特工之间互换位置不算。

输入描述:

第一行包含空格分隔的两个数字N和D(1<=N<=1000000, 1<=D<=N)。

第二行包含N个建筑物的位置,每个位置用一个整数表示,从小到大排列(将字节跳动大街看做一个数组)

输出描述:

一个数字,表示不同埋伏方案的数量。结果可能溢出,请对999……(博主:照片没拍到,忘了是什么了)取模。

示例:

输入 输出

4 3

1 2 3 4

4

分析:

最大距离D确定后,可埋伏区间必为某段连续区间。从左到右扫描整个数组,记录每个位置埋伏一个人使,其余两人在右边埋伏共有多少种可能,最终全部相加得到结果。

代码:

a = input().split()
b = input().split()
n, d = int(a[0]), int(b[0])
alist = [int(x) for x in b]
#n, d = 7, 3
#alist = [1, 5, 6, 7, 8, 12, 13]

ambush_kinds = []          # 记录第一个人埋伏在当前位置时,其他人的埋伏的种类
start = 0        # 指向第一个埋伏的人
end = 0        # 指向最后一个人最远埋伏距离

# 定义组合函数C_n^k
def c(n, k):
    numerator = 1
    denominator = 1
    for i in range(k):
        numerator *= n-i
        denominator *= k-i
    return numerator / denominator

while start < n-2:                      # 终止条件:三个人埋伏在最后三个位置
    # 找到当前start对应的end的最远位置
    while end < n and alist[end] - alist[start] <= d:
        end += 1
    ambush_range = end - start          # 计算三人埋伏的范围
    if ambush_range < 3:                # 如果此范围太小不够三人埋伏,计数为0
        ambush_kinds.append(0)
    else:                               # 一人埋伏在start位置时,取余两人的埋伏种类
        ambush_kinds.append(c(ambush_range-1, 2))
    start += 1                          # 计算下一个start
print(sum(ambush_kinds))
#print(ambush_kinds)

四、最长全1区间

题目:

给定一个长度为n的,仅包含0,1的数列。例如1,0,0,1,1,1,0。我们可以轻易算出,它的最长全1区间长度是3。从数组的第4位到第6位。

现在,你获得了k次可以将某个位置上的0变为1的机会,但可以不用完所有的机会。

请你给出,你使用了你的变化机会后,这个数列的最长全1区间最大是多少

分析:

要使最大全1区间最长,一定是将某段连续区间内的0变为1。由此,可从左到右扫描整个数组,记录每个位置向右依次将0变为1后的最大全1区间长度。最后求整个数组的最大值。

上代码:

a = input().split()
b = input().split()
n, k = int(a[0]), int(b[0])
alist = [int(x) for x in b]
#n, k = 4, 1
#alist = [0, 1, 0, 1]

maxlen = []
end = 0        # 指向全1区间的结尾
start = 0        # 指向全1区间的开头

while end < n:                      # 结束条件:end扫到末尾
    # 找到当前start对应的end的最远位置
    while k >= 0 and end < n:
        if alist[end] == 1:         # 如果是1,继续走
            end += 1
            continue
        k -= 1                      # 如果是0,消耗一次0到1的变化机会
        end += 1
    maxlen.append(end - start)      # 记录此时的最长全1区间
    
    # start向前挪动一位
    if alist[start] == 0:           # 如果挪动前start是0,则可节省1次01变化
        k = 1
    start += 1
print(max(maxlen))

猜你喜欢

转载自blog.csdn.net/qq_26271435/article/details/89600949