京东实习生招聘题目解析(三)

 阅读约 16 分钟

有段时间没写了,但是之前把JD剩余的题目基本还是过了一次,题目总体都比较简单,本次的解析选择了其中的5个题目,由于比较简单,就分析的简略些,留一些思考的空间。因为很久没有更新,最近还会和大家讨论一个动态规划解决的简单博弈问题。


站队

<题目来源: 京东2017秋招 原题链接-可在线提交(赛码网) >

问题描述

一条很长的队伍,队伍里面一共有n个人。所有的人分为三类:警察,小偷和普通人。将队伍里面的人从前到后由1到n编号,编号为i的人与编号为j的人的距离为i与j之差的绝对值。

每一个警察有一个能力值x,表示他能够监视与他距离不超过x的所有人,小偷被警察发现当且仅当他被一个或多个警察监视到。你知道在整条队伍中,一共有多少个小偷会被警察发现吗?

clipboard.png

题目意思比较简单,最容易想到就是扫描一次这个队伍,如果是警察,就检查其能力范围内是否有小偷,如果有小偷就给小偷打上标记,这样是为了防止重复统计。当执行完本次扫描后就得到了最后的结果。

观察题目的数据规模,n<=100000,而每个警察能力值是1-9,最多需要扫描前后共计18个人。显然最坏情况下的运算也不过百万级,是完全可以在1s内出解的。

那么我们来思考下如果不限制警察的范围,那么这样做显然就不可行了。这样最坏情况可以是O(n^2),显然是可接受的。问题出在哪里呢?是因警察覆盖的范围出现了大量的重复。比如A警察在x=100的地方,B在x=110的地方,他们能力值都是100,显然对绝大多数位置进行了多次扫描。

如果要提高程序效率我们就要尽可能的减少重复的扫描,那么我们首先想到的是在加一个标记,表示当前扫描到了哪个位置,下个警察再进行扫描时,如果扫描到了该位置就不再继续扫描了。考虑下面的情况:
......------(1)------..............---(2)---........................
---------------------------(3)---------------------------
其中括号内的数字表示警察,"-"是其能力范围,..可以表示小偷或者普通人。

显然,记录最后扫描位置并不是一种好的方法。我们希望找到所以警察能力值不重复覆盖的范围,联系到线段覆盖问题,又或者是流水线作业问题等,我们得到一种处理方法,把每个警察和其能力范围看做是一条线段,把所有的这些线段按左端点排序,然后依次处理右侧的值。考虑当前线段右侧的位置和下条线段左侧的位置即可。

import sys


def main():
    n = map(int, sys.stdin.readline().strip().split())[0]
    line = list(map(str, sys.stdin.readline().strip().split())[0])

    segments = []
    for i, ch in enumerate(line):
        if '1' <= ch <= '9':
            t = int(ch) - int('0')
            left = max(0, i - t)
            right = min(n - 1, i + t)
            segments.append((left, right))

    segments = sorted(segments, key=lambda s: s[0])
    #print segments

    current = -1
    res = 0
    for segment in segments:
        if segment[0] <= current:
            l = current + 1
            r = segment[1]
        else:
            l = segment[0]
            r = segment[1]

        for i in range(l, r + 1):
            if line[i] == 'X':
                res += 1

        if r > current:
            current = r

    print res


if __name__ == '__main__':
    main()

第K个幸运数

<题目来源: 京东2017实习生招聘 原题链接-可在线提交(赛码网) >

问题描述

4和7是两个幸运数字,我们定义,十进制表示中,每一位只有4和7两个数的正整数都是幸运数字。前几个幸运数字为:4,7,44,47,74,77,444,447...

现在输入一个数字K,输出第K个幸运数。

clipboard.png

先按顺序列出一些幸运数(下划线是为了从低位开始对齐):
___4
___7
__44
__47
__74
__77
_444
_447
_474
_477
_744
_747
_774
_777
4444
...

观察规律发现:1位的2个,2位的4个,3位的8个,推测n位的幸运数有2^k个
再来观察低位规律 4, 7, 4, 7...
次低位的规律4, 4, 7, 7, 4, 4, 7, 7...
右侧第三位的规律,4, 4, 4, 4, 7, 7, 7, 7...
那么我们就可以依靠这些规律来计算每一位的数字,需要注意的是在计算第k位的时候,前k-1位是需要去掉的。

第t个幸运数从低位开始第i位上的数字是r = (t - 2^i - 2) % 2^i
当 1 <= r <= 2^i / 2 时为'4',否则为'7'

import sys


def main():
    case = map(int, sys.stdin.readline().strip().split())[0]
    for c in range(case):
        n = map(int, sys.stdin.readline().strip().split())[0]

        sum2s = 1
        num = []
        while True:
            sum2s *= 2
            if n - sum2s + 2 > 0:
                r = (n - sum2s + 2) % sum2s
                if 1 <= r <= sum2s / 2:
                    num.insert(0, '4')
                else:
                    num.insert(0, '7')
            else:
                break

        print ''.join(num)


if __name__ == "__main__":
    main()

选举游戏

<题目来源: 京东2016实习生招聘 原题链接-可在线提交(赛码网) >

问题描述

小东和其他小朋友正在玩一个关于选举的游戏。选举是通过投票的方式进行的,得票最多的人将获胜。

小东是编号为1的候选者,此外还有其他的候选者参加选举。根据初步的调查情况,所有准备投票的小朋友都有一定的投票倾向性,小东如果要获得胜利,必须争取部分准备为其他候选人投票的小朋友。

由于小东的资源较为有限,她希望用最小的代价赢得胜利,请你帮忙计算她最少需要争取的选票数。

clipboard.png

要小东能获胜,就要确保他自己的票超过另外所有人的票,就要从其他的候选者那里拿票。为了尽可能少的从其他的人手中拿票,那么应该尽可能的从比他多且最多的人手中拿票。注意到拿票是2个动作,小东会多一票,而被拿票的后选择会少一票。
问题就解法就是始终从票最多的人那里拿票(人会发生变化),直到自己的票数刚好成为最多的时候。

堆是一个能很好支撑这个要求的数据结构,如果不清楚堆(heap)可以查询相关资料。下面的代码使用了heapq来实现。

import sys
import heapq


def main():
    while True:
        line = map(int, sys.stdin.readline().strip().split())
        if len(line) < 0:
            break

        n = line[0]
        heap = []
        line = map(int, sys.stdin.readline().strip().split())

        for i, l in enumerate(line):
            if i:
                heapq.heappush(heap, -l)

        cnt = 0
        while True:
            e = -heapq.nsmallest(1, heap)[0]
            if line[0] > e:
                break
            line[0] += 1
            cnt += 1
            e -= 1
            heapq.heapreplace(heap, -e)

        print cnt


if __name__ == '__main__':
    main()

交易清单

<题目来源: 京东2016实习生招聘 原题链接-可在线提交(赛码网) >

问题描述

金融证券行业超好的薪酬待遇,吸引了大批的求职者前往应聘,小东也不例外,准备应聘一家证券公司。面试官为考察她的数据分析、处理和编码能力,为她准备了以下问题。

股票交易中,委托是指股票交易者在证券公司买卖股票。每手委托包括一个委托单号i、价格pi、买入或卖出标记di及交易数量qi。

交易处理中,需要把同类业务(买入或卖出)中相同价格的所有委托合并起来,形成一个清单。清单的第一部分为按价格降序排列的合并后的卖出委托,紧随其后的是按相同顺序排列的买入合并委托。证券公司比较关心的是比较抢手的s条合并委托信息,需要得到买入及卖出最抢手的s条合并委托。对于买入委托,抢手的是指报价高的委托,而卖出委托中报价低的较为抢手。若买或卖的合并委托数小于s条,则全部列入清单中。

现在小东拿到的是n个委托,请你帮忙找出最抢手的s个合并委托。

clipboard.png

这个题目本身不难,但是一定要读懂题意,该如何合并数据,该如何排序,如何取排序后的top n。这个题目可以锻炼快速编程的能力。
例如合并价格相同的委托,可以采用多种方式,我这里使用python,为了尽可能的快速,使用了dict。然后分别再对买入和卖出的委托进行排序。

import sys


def main():
    while True:
        line = map(int, sys.stdin.readline().strip().split())
        if len(line) < 2:
            break

        n, s = line[0], line[1]

        dict_b = {}
        dict_s = {}
        for i in range(n):
            line = map(str, sys.stdin.readline().strip().split())

            if line[0] == 'B':
                r = dict_b.get(line[1])
                if r is None:
                    dict_b[line[1]] = int(line[2])
                else:
                    dict_b[line[1]] = r + int(line[2])
            else:
                r = dict_s.get(line[1])
                if r is None:
                    dict_s[line[1]] = int(line[2])
                else:
                    dict_s[line[1]] = r + int(line[2])

        list_b = []
        list_s = []
        for k, v in dict_b.iteritems():
            list_b.append((int(k), v))
        list_b = sorted(list_b, reverse=True)

        for k, v in dict_s.iteritems():
            list_s.append((int(k), v))
        list_s = sorted(list_s)

        p = min(s, len(list_s))
        for e in list_s[p::-1]:
            print 'S', e[0], e[1]

        p = min(s, len(list_b))
        for e in list_b[:p]:
            print 'B', e[0], e[1]


if __name__ == '__main__':
    main()

进制均值

<题目来源: 京东2017秋招 原题链接-可在线提交(赛码网) >

问题描述

尽管是一个CS专业的学生,小B的数学基础很好并对数值计算有着特别的兴趣,喜欢用计算机程序来解决数学问题。现在,她正在玩一个数值变换的游戏。她发现计算机中经常用不同的进制表示同一个数,如十进制数123表达为16进制时只包含两位数7、11(B),用八进制表示时为三位数1、7、3。按不同进制表达时,各个位数的和也不同,如上述例子中十六进制和八进制中各位数的和分别是18和11。
小B感兴趣的是,一个数A如果按2到A-1进制表达时,各个位数之和的均值是多少?她希望你能帮她解决这个问题?
所有的计算均基于十进制进行,结果也用十进制表示为不可约简的分数形式。

clipboard.png

这个题目考察进制转换,利用短除法可以计算任何进制的转换。此外,要掌握一到两种最大公约数(gcd)的计算方法。

import sys


def gcd(a, b):
    c = a % b
    if c == 0:
        return b

    return gcd(b, c)


def main():
    while True:
        n = map(int, sys.stdin.readline().strip().split())[0]

        rest_sum = 0
        for i in range(2, n):
            temp = n
            while temp:
                rest_sum += temp % i
                temp /= i

        g = gcd(rest_sum, n - 2)

        print("%d/%d" % (rest_sum / g, (n - 2) / g))

if __name__ == '__main__':
    main()
阅读 1k发布于 2017-10-26

推荐阅读
Lite's home
用户专栏

和大家分享一些经典的算法,以及解决问题的思路和方法

1 人关注
12 篇文章
专栏主页
目录