【笔试代码题记录】20190810贝壳/网易互娱

2019.8.10 贝壳

2. 丢失的卡片

3. 锯子斧头

面前有n颗树,你有两种工具锯子和斧头,它们砍第i颗树的时间分别为ai和bi,一开始你拿的是斧头,而砍第i颗树前交换工具需要花费ci的时间,请问,看完这些树的时间最短是多少?

输入:
第一行一个数n(1<=100)
接下来n行,每行3个数ai,bi,ci(<=30000)
输出:最短时间

样例输入:
3
20 40 20
10 4 25
90 100 5

样例输出:
139

解法(正确解法):双状态的动态规划。 可以参考下leetcode714 带手续费的买卖股票问题。

def GetResult(record, n):
    dp = [[0] * 2 for _ in range(n)]
    # 第一棵树,第二维0,1表示斧头和锯子
    dp[0][0] = record[0][1] # 表示本轮用斧子,花费的最少时间
    dp[0][1] = record[0][0] + record[0][2]  # 表示本轮用锯子,花费的最少时间
    for i in range(1, n):
        dp[i][0] = min(dp[i - 1][0] + record[i][1], dp[i - 1][1] + record[i][1] + record[i][2])
        dp[i][1] = min(dp[i - 1][1] + record[i][0], dp[i - 1][0] + record[i][0] + record[i][2])
    print(dp)
    return min(dp[-1][0], dp[-1][1])

反思:自己没有想到用双状态的dp,而是使用了一个flag变量来表示当前的工具,没通过。。

4. 图的存在性

给定过一个1到N的排列 P 1 P_1 P1- P N P_N PN,请判断是否存在一个由N个点,N-1条边组成的无向连通图,满足对于任意两个整数i和j(i不等于j),若第i个点和第j个点之间有边相连,则第 P i P_i Pi个点和第 P j P_j Pj个点之间同样有边相连。

输入:
第一行输入一个整数T表示T组数据,
每组数据格式为:
第一行包括一个整数N,
第二行包含N个整数 P 1 P_1 P1 P N P_N PN

输出:
每组输出一行,如果存在满足条件的图则输出Yes,否则输出No

样例输入:

2
4
4 3 2 1
3
3 1 2

样例输出:

Yes
No

2019.8.11网易互娱

1. 转二进制后有多少个1 ,个数相同归为一类,问一共几类?

本题AC,思路:遍历给出的数字,统计每个数字的二进制中1的个数,讲这个个数存入集合中,最后输出集合的大小即可。

T=int(input()) for i in range(0,T):
    N=int(input())
    nums=list(map(int,input().split()))
    res={} for x in nums:
        temp="{0:b}".format(x) print(temp)
        t=temp.count('1') if t not in res:
            res[t]=1  else:
            res[t]+=1  print(len(res))

2. 注水排水问题(没做出来)

水池容量为m,有注水管和排水管,刚开始两个管子都是开的,水池里没有水。每隔t1分钟,注水管状态改变一次,开变关,关变开;每隔t2分钟排水管状态改变一次,注水管在打开时,一分钟注水m1,排水管一分钟出水m2,问,t分钟后,水池里的水有多少?(1<=T<=10, 1<=m<=100000, 1<= t <= 86400, 1 <= m1,m2 <=100, 1<= t1,t2 <= 10)

测试样例(几个参数分别是m, t, m1, t1, m2, t2):

10,2,1,5,2,5  # 输出0
10,2,10,5,2,5 # 10
10 2 3 5 2  5 # 2
100 100 3 4 4 3  # 3
10000 1000 10 5 5 3  # 2495

本题的暴力解法是:按时间从0开始遍历,每分钟判断两个水管的开关状态,然后每分钟更新一次水量,直至t时刻。
本题的优化解法是:循环一个周期性,周期性就是t1和t2的最小公倍数,循环这么多次,就统计出了在一个周期内池里剩余的水量,每个周期剩余的水量都是相同的。最后在单独处理t%周期性求余最后部分单独求解,可以减小计算量。

由于本题的暴力解法可以通过,因此牛客上的AC代码大都是暴力解法,这里列出一个AC代码

def _clip(v, v_min, v_max): # 保证水不超过泳池限额
    if v > v_min and v < v_max:
        return v
    elif v < v_min:
        return v_min
    else:
        return v_max

def _update_water(M, on, off, m, m1, m2): # 每分钟更新一次水量
    if on and off:
        return _clip(M + (m1 - m2), 0, m)
    elif on:
        return _clip(M + m1, 0, m)
    elif off:
        return _clip(M - m2, 0, m)
    else:
        return M

def f(nums): # 主函数,返回最终水量即可
    m, t, m1, t1, m2, t2 = nums
    M, T = 0, 0 # 刚开始的水量和时间都是0
    while T < t:
        on, off = T // t1 % 2 == 0, T // t2 % 2 == 0 # 这行代码是精髓,判断当前时刻两个水管分别是开还是关
        M = _update_water(M, on, off, m, m1, m2)
        T += 1
    return M

3. 最长重复字符串(AC90%)

给定字符串,最多允许改变两个字符,使得连续“N”串最长,求最长的连续N串的长度。

我的解法(AC 90%,至今不知道剩下的10%是哪里错了):遍历字符串,统计所有非“N”字符的下标在一个列表res中,然后将0和字符串长度len(s)添加到res的首尾(这样可以一起处理边界问题,类似于链表里面的dummy_head思想),遍历res列表,统计res[i] - res[i - 3]的最大值(相当于将res[i - 2]和res[i - 1]替换掉),输出最终结果。

测试用例:

s = 'NGNNNNGNNNNNNNNSNNNN' # 18
s = 'NNNNGGNNNN'  # 10
s = "NNTN"  # 4 
def func(s):
    if not s or len(s) <= 2:
        return len(s)
    queue = [] if s[0] == 'N' else [0]
    for i in range(1, len(s)):
        if s[i] != 'N':
            queue.append(i)
    if len(queue) <= 2: return len(s)
    if queue[-1] < len(s): queue.append(len(s))
    if queue[0] > 0: queue.insert(0,0)
    res = 0
    for i in range(3, len(queue)):
        res = max(res, queue[i] - queue[i- 3] - 1)
    return res
    
import sys
if __name__ == "__main__":
    while True:
        try:
            t = int(sys.stdin.readline().strip())
            if t:
                for i in range(t):
                    s = input()
                    print(func(s))
            else:
                break
        except:
            break

网上的AC代码(动态规划):维护数组dp以及v_dp,dp保存要用到的索引,v_dp保存以当前字符结尾的最长连续字串长度(可容错2),最后返回v_dp的最大值即可. 个人感觉不那么容易理解。

def f(S):
    n = len(S)
    dp = [[-1, 0, 0] for _ in range(n)]
    v_dp = [0 for _ in range(n)]
    cnt = 0 # 用来统计替换了几次
    for i in range(n):
        if S[i] != 'N' and cnt == 0:
            dp[i][1] = i
            cnt += 1
        elif S[i] != 'N' and cnt == 1:
            dp[i][1] = dp[i - 1][1]
            dp[i][2] = i
            cnt += 1
        elif S[i] != 'N' and cnt == 2: # 如果两次机会都替换了,则将最前面替换的“挤掉”
            dp[i] = dp[i - 1][1:] + [i]
        else:
            dp[i] = dp[i - 1]
        v_dp[i] = i - dp[i][0]
    return max(v_dp)

AC代码二(双指针+计数,思路很清晰,重要):双指针左右夹逼,夹逼区间的宽的最大值即为所求。计数器count记录非N字符的个数,当计数器超过2时,左指针后移,一直移到计数器不超过2为止。时间复杂度O(n) 【参考】

def f(s):
    if not s or len(s) <= 2:
        return len(s)
    left, right = 0, 0
    count, res = 0, 0 # count用来统计替换的次数
    while right < len(s):
        if s[right] != 'N':
            count += 1
        right += 1    
        while count > 2:
            if s[left] != 'N':
                count -= 1
            left += 1

        res = max(res, right - left)
    return res
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值