2018年第九届蓝桥杯省赛真题解题报告(Python版)-2

13 航班时间

提示:正则表达式

'''

标题:航班时间

【问题背景】
小h前往美国参加了蓝桥杯国际赛。小h的女朋友发现小h上午十点出发,上午十二点到达美国,于是感叹到“现在飞机飞得真快,两小时就能到美国了”。

小h对超音速飞行感到十分恐惧。仔细观察后发现飞机的起降时间都是当地时间。由于北京和美国东部有12小时时差,故飞机总共需要14小时的飞行时间。

不久后小h的女朋友去中东交换。小h并不知道中东与北京的时差。但是小h得到了女朋友来回航班的起降时间。小h想知道女朋友的航班飞行时间是多少。

【问题描述】
对于一个可能跨时区的航班,给定来回程的起降时间。假设飞机来回飞行时间相同,求飞机的飞行时间。

【输入格式】
从标准输入读入数据。

一个输入包含多组数据。

输入第一行为一个正整数T,表示输入数据组数。

每组数据包含两行,第一行为去程的 起降 时间,第二行为回程的 起降 时间。

起降时间的格式如下

h1:m1:s1 h2:m2:s2
或
h1:m1:s1 h3:m3:s3 (+1)
或
h1:m1:s1 h4:m4:s4 (+2)
表示该航班在当地时间h1时m1分s1秒起飞,

第一种格式表示在当地时间 当日 h2时m2分s2秒降落

第二种格式表示在当地时间 次日 h3时m3分s3秒降落。

第三种格式表示在当地时间 第三天 h4时m4分s4秒降落。

对于此题目中的所有以 h:m:s 形式给出的时间, 保证 ( 0<=h<=23, 0<=m,s<=59 ).

【输出格式】
输出到标准输出。

对于每一组数据输出一行一个时间hh:mm:ss,表示飞行时间为hh小时mm分ss秒。

注意,当时间为一位数时,要补齐前导零。如三小时四分五秒应写为03:04:05。

【样例输入】
3
17:48:19 21:57:24
11:05:18 15:14:23
17:21:07 00:31:46 (+1)
23:02:41 16:13:20 (+1)
10:19:19 20:41:24
22:19:04 16:41:09 (+1)

【样例输出】
04:09:05
12:10:39
14:22:05

【限制与约定】
保证输入时间合法,飞行时间不超过24小时。

资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗  < 1000ms


请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。

'''
N = 0

import re

p1 = re.compile(r'(\d+):(\d+):(\d+) (\d+):(\d+):(\d+)')
p2 = re.compile(r'(\d+):(\d+):(\d+) (\d+):(\d+):(\d+) \(\+(\d)\)')


def getTime(s):
    '''

    :param s:
    :return: 两个时间差,单位为秒
    '''
    h1, m1, s1, h2, m2, s2, d = 0, 0, 0, 0, 0, 0, 0
    matchObj = p1.fullmatch(s)
    # 匹配第一种模式
    if matchObj:
        h1, m1, s1, h2, m2, s2 = (int(x) for x in matchObj.groups())
    # 匹配第二种模式
    else:
        matchObj = p2.fullmatch(s)
        h1, m1, s1, h2, m2, s2, d = (int(x) for x in matchObj.groups())

    return d * 24 * 3600 + h2 * 3600 + m2 * 60 + s2 - (h1 * 3600 + m1 * 60 + s1)


if __name__ == '__main__':
    N = int(input().strip())
    for i in range(N):
        t1 = getTime(input())
        t2 = getTime(input())
        t = (t1 + t2) // 2
        print('{:0>2d}:{:0>2d}:{:0>2d}'.format(t // 3600, t // 60 % 60, t % 60))

14 递增三元组

提示:排序

'''

标题:递增三元组

给定三个整数数组
A = [A1, A2, ... AN],
B = [B1, B2, ... BN],
C = [C1, C2, ... CN],
请你统计有多少个三元组(i, j, k) 满足:

1. 1 <= i, j, k <= N
2. Ai < Bj < Ck

【输入格式】
第一行包含一个整数N。
第二行包含N个整数A1, A2, ... AN。
第三行包含N个整数B1, B2, ... BN。
第四行包含N个整数C1, C2, ... CN。

对于30%的数据,1 <= N <= 100
对于60%的数据,1 <= N <= 1000
对于100%的数据,1 <= N <= 100000 0 <= Ai, Bi, Ci <= 100000

【输出格式】
一个整数表示答案

【输入样例】
3
1 1 1
2 2 2
3 3 3

【输出样例】
27


资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗  < 1000ms
'''

if __name__ == '__main__':
    '''
    对三个数组排序
    以中间数组为基准,在第一个数组中寻找比当前数小的数据个数p1
    在第三个数组中寻找比当前数小于等于的数据个数进而得到大于当前数的数据个数为N-p2
    两数相乘即得当前数为中心的三元组的个数,累加到ans即可
    '''
    N = int(input().strip())
    data = [[int(x) for x in input().strip().split(' ')] for _ in range(3)]
    d0 = sorted(data[0])
    d1 = sorted(data[1])
    d2 = sorted(data[2])
    ans, p1, p2 = 0, 0, 0
    for j in range(N):
        while p1 < N and d0[p1] < d1[j]:
            p1 += 1
            if p1 == N:
                break
        while p2 < N and d2[p2] <= d1[j]:
            p2 += 1
            if p2 == N:
                break
        ans += p1 * (N - p2)
    print(ans)

15 次数差

提示:很简单的计数

'''

标题:次数差

x星球有26只球队,分别用a~z的26个字母代表。他们总是不停地比赛。
在某一赛段,哪个球队获胜了,就记录下代表它的字母,这样就形成一个长长的串。
国王总是询问:获胜次数最多的和获胜次数最少的有多大差距?(当然,他不关心那些一次也没获胜的,认为他们在怠工罢了)

输入,一个串,表示球队获胜情况(保证串的长度<1000)

要求输出一个数字,表示出现次数最多的字母比出现次数最少的字母多了多少次。

比如:
输入:
abaabcaa

则程序应该输出:
4

解释:a出现5次,最多;c出现1次,最少。5-1=4

再比如:
输入:
bbccccddaaaacccc

程序应该输出:
6



资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗  < 1000ms


请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。

'''
from collections import defaultdict

if __name__ == '__main__':
    line = input()
    '''
    cnt = defaultdict(int)
    # 计数
    for x in line:
        cnt[x] += 1
    # 按值排序,返回2元组的列表
    sorted_cnt=sorted(cnt.items(), key=lambda item: item[1])
    print(sorted_cnt[-1][1] - sorted_cnt[0][1])
    '''

    from collections import Counter
    '''
    Counter是个好东西,专用于计数
    '''
    count = Counter(line).most_common()
    print(count[0][1] - count[-1][1])

16 三体攻击

提示:比较复杂,要用三维差分并二分攻击次数

'''

标题:三体攻击

【题目描述】
三体人将对地球发起攻击。为了抵御攻击,地球人派出了 A × B × C 艘战舰,在太空中排成一个 A 层 B 行 C 列的立方体。其中,第 i 层第 j 行第 k 列的战舰(记为战舰 (i, j, k))的生命值为 d(i, j, k)。

三体人将会对地球发起 m 轮“立方体攻击”,每次攻击会对一个小立方体中的所有战舰都造成相同的伤害。具体地,第 t 轮攻击用 7 个参数 lat, rat, lbt, rbt, lct, rct, ht 描述;
所有满足 i ∈ [lat, rat],j ∈ [lbt, rbt],k ∈ [lct, rct] 的战舰 (i, j, k) 会受到 ht 的伤害。如果一个战舰累计受到的总伤害超过其防御力,那么这个战舰会爆炸。

地球指挥官希望你能告诉他,第一艘爆炸的战舰是在哪一轮攻击后爆炸的。

【输入格式】
从标准输入读入数据。
第一行包括 4 个正整数 A, B, C, m;
第二行包含 A × B × C 个整数,其中第 ((i − 1)×B + (j − 1)) × C + (k − 1)+1 个数为 d(i, j, k);
第 3 到第 m + 2 行中,第 (t − 2) 行包含 7 个正整数 lat, rat, lbt, rbt, lct, rct, ht。

【输出格式】
输出到标准输出。
输出第一个爆炸的战舰是在哪一轮攻击后爆炸的。保证一定存在这样的战舰。

【样例输入】
2 2 2 3
1 1 1 1 1 1 1 1
1 2 1 2 1 1 1
1 1 1 2 1 2 1
1 1 1 1 1 1 2

【样例输出】
2

【样例解释】
在第 2 轮攻击后,战舰 (1,1,1) 总共受到了 2 点伤害,超出其防御力导致爆炸。


【数据约定】
对于 10% 的数据,B = C = 1;
对于 20% 的数据,C = 1;
对于 40% 的数据,A × B × C, m ≤ 10, 000;
对于 70% 的数据,A, B, C ≤ 200;
对于所有数据,A × B × C ≤ 10^6, m ≤ 10^6, 0 ≤ d(i, j, k), ht ≤ 10^9。


资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗  < 3000ms


请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。

'''

# 思路:
# 1)范围攻击用差分记录,这样每次攻击只需花O(1)的时间来记录
# 2)二分攻击次数,攻破了,就在下半区二分回复血量,没攻破就在上半区再二分继续攻击 O(logm)
# 3)每次检查是否攻破,求差分前缀和求最终血量,再遍历ABC个格子,看是否有血量小于0来判断 O(ABC)

from copy import copy

a, b, c = 0, 0, 0


def getX(i, j, k):
    '''
    :param i:
    :param j:
    :param k:
    :return: 转为1维索引
    '''
    return ((i - 1) * b + j - 1) * c + k


def add(cc, i, j, k, h):
    '''
    修改ijk坐标的差分数组的值
    :param cc:
    :param i:
    :param j:
    :param k:
    :param h:
    :return:
    '''
    if i < a and j < b and k < c:
        cc[getX(i, j, k)] += h


def op(cc, atk, f):
    '''
    :param cc: 差分数组
    :param atk: 元组,数据是一次攻击的相关参数
    :param f: 1或者-1,代表攻击还是回复血量
    :return: void
    '''
    la, ra, lb, rb, lc, rc, h = atk
    h = h * f
    add(cc, la, lb, lc, h)
    add(cc, la, rb + 1, lc, -h)
    add(cc, la, lb, rc + 1, -h)
    add(cc, la, rb + 1, rc + 1, h)
    add(cc, ra + 1, lb, lc, -h)
    add(cc, ra + 1, rb + 1, lc, h)
    add(cc, ra + 1, lb, rc + 1, h)
    add(cc, ra + 1, rb + 1, rc + 1, -h)


def check(cc, d):
    '''
    三个维度累加,得差分数组的前缀和,就能得到每个点上的当前血量的变化量
    :param cc:
    :param d:
    :return:
    '''
    # 差分数组的前缀和,来自差分数组的复制
    _sum = copy(cc)
    for i in range(2, a):
        for j in range(1, b):
            for k in range(1, c):
                _sum[getX(i, j, k)] += _sum[getX(i - 1, j, k)]
    for i in range(1, a):
        for j in range(2, b):
            for k in range(1, c):
                _sum[getX(i, j, k)] += _sum[getX(i, j - 1, k)]
    for i in range(1, a):
        for j in range(1, b):
            for k in range(2, c):
                _sum[getX(i, j, k)] += _sum[getX(i, j, k - 1)]

    # 遍历每个点,检查变化量是否大于原始血量
    for i in range(1, a):
        for j in range(1, b):
            for k in range(1, c):
                index = getX(i, j, k)
                if _sum[index] > d[index]:
                    return True
    return False


if __name__ == '__main__':
    # import sys
    #
    # sys.stdin = open('/Users/zhengwei/data/in6.txt')
    A, B, C, m = (int(x) for x in input().strip().split(' '))
    a, b, c = A + 1, B + 1, C + 1
    _d = [int(x) for x in input().strip().split(' ')]  # 原始血量数据
    # 统一为三维情况
    d = [0 for _ in range(a * b * c)]  # 原始血量(为了便于从1开始,增加长宽高,可用ijk定位)
    index = 0
    for i in range(1, a):
        for j in range(1, b):
            for k in range(1, c):
                d[getX(i, j, k)] = _d[index]
                index += 1

    cc = [0 for _ in range(a * b * c)]  # 差分数组
    # 攻击数据
    atk = [tuple()]  # 注意,下标从1开始,所以要占位一个空的元组作为0号元素
    for i in range(m):
        atk.append(tuple(int(x) for x in input().strip().split(' ')))
        # print(atk[i])

    # 二分攻击次数
    l, r, lastMid = 1, m, 0
    while l < r:
        mid = (l + r) >> 1
        if lastMid < mid:  # 上一次攻击没有攻破,继续攻击
            for i in range(lastMid + 1, mid + 1):
                op(cc, atk[i], 1)  # 修改差分数组
        elif lastMid > mid:  # 上一次攻破了,恢复血量
            for i in range(mid + 1, lastMid + 1):
                op(cc, atk[i], -1)
        # 如果爆了,调整边界
        if check(cc, d):
            r = mid
        else:
            l = mid + 1
        lastMid = mid
    print(r)

17 螺旋折线

提示:以右下角对角线上的点为参照点(等差数列),测算给定的点到参照点要走的距离

'''
标题:螺旋折线

如图p1.png所示的螺旋折线经过平面上所有整点恰好一次。
对于整点(X, Y),我们定义它到原点的距离dis(X, Y)是从原点到(X, Y)的螺旋折线段的长度。

例如dis(0, 1)=3, dis(-2, -1)=9

给出整点坐标(X, Y),你能计算出dis(X, Y)吗?

【输入格式】
X和Y

对于40%的数据,-1000 <= X, Y <= 1000
对于70%的数据,-100000 <= X, Y <= 100000
对于100%的数据, -1000000000 <= X, Y <= 1000000000

【输出格式】
输出dis(X, Y)


【样例输入】
0 1

【样例输出】
3


资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗  < 1000ms


请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。
'''


def _sum(a0, n, d):
    return (2 * a0 + (n - 1) * d) * n // 2


if __name__ == '__main__':
    # 以右下角对角线上的点为参照点,测算给定的点到参照点要走的距离
    X, Y = (int(i) for i in input().strip().split(' '))
    d, n = 0, 0  # 距离和第几圈
    if Y > 0 and abs(X) <= Y:
        n = Y
        d = Y - X + 2 * Y
    elif X > 0 and abs(Y) <= X:
        n = X
        d = Y + X
    elif Y <= 0 and Y - 1 <= X <= -Y:
        n = -Y
        d = -(-Y - X)
    elif X < 0 and X + 1 <= Y <= -X:
        n = -X - 1
        d = -(Y - X - 1 - 2 * X - 1)
    print(_sum(1, 2 * n, 1) * 2 - d)

18 缩位求和

简单题

'''
标题:缩位求和

在电子计算机普及以前,人们经常用一个粗略的方法来验算四则运算是否正确。
比如:248 * 15 = 3720
把乘数和被乘数分别逐位求和,如果是多位数再逐位求和,直到是1位数,得
2 + 4 + 8 = 14 ==> 1 + 4 = 5;
1 + 5 = 6
5 * 6
而结果逐位求和为 3
5 * 6 的结果逐位求和与3符合,说明正确的可能性很大!!(不能排除错误)

请你写一个计算机程序,对给定的字符串逐位求和:
输入为一个由数字组成的串,表示n位数(n<1000);
输出为一位数,表示反复逐位求和的结果。

例如:
输入:
35379

程序应该输出:
9

再例如:
输入:
7583676109608471656473500295825

程序应该输出:
1


资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗  < 1000ms


请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。

'''

if __name__ == '__main__':
    s = input()
    while len(s) > 1:
        s = str(sum(int(x) for x in s))
    print(s)

19 全球变暖

提示:基本就是bfs的模板题

'''
标题:全球变暖

你有一张某海域NxN像素的照片,"."表示海洋、"#"表示陆地,如下所示:

.......
.##....
.##....
....##.
..####.
...###.
.......

其中"上下左右"四个方向上连在一起的一片陆地组成一座岛屿。例如上图就有2座岛屿。

由于全球变暖导致了海面上升,科学家预测未来几十年,岛屿边缘一个像素的范围会被海水淹没。具体来说如果一块陆地像素与海洋相邻(上下左右四个相邻像素中有海洋),它就会被淹没。

例如上图中的海域未来会变成如下样子:

.......
.......
.......
.......
....#..
.......
.......

请你计算:依照科学家的预测,照片中有多少岛屿会被完全淹没。

【输入格式】
第一行包含一个整数N。  (1 <= N <= 1000)
以下N行N列代表一张海域照片。

照片保证第1行、第1列、第N行、第N列的像素都是海洋。

【输出格式】
一个整数表示答案。


【输入样例】
7
.......
.##....
.##....
....##.
..####.
...###.
.......

【输出样例】
1



资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗  < 1000ms

'''


class Point(object):
    def __init__(self, i, j):
        self.i = i
        self.j = j


ans = 0
graph, mark = [], []

dx = (-1, 1, 0, 0)
dy = (0, 0, -1, 1)


def bfs(i, j):
    mark[i][j] = 1
    cntOfBlock, cntOfSwed = 0, 0 # 记录块的个数以及会被淹没的块的个数
    q, q_index = [], 0 # 队列及队列头部的索引位置
    q.append(Point(i, j))
    while q_index != len(q):
        top = q[q_index]
        q_index += 1
        cntOfBlock += 1
        swed = False
        for k in range(4):  # 四个方向去探测
            nx, ny = top.i + dx[k], top.j + dy[k]
            if 0 <= nx < N and 0 <= ny < N:
                if graph[nx][ny] == '.':  # 会被淹没
                    swed = True
                if graph[nx][ny] == '#' and mark[nx][ny] == 0:  # 邻居中的#如果没有被访问,加入到队列中
                    mark[nx][ny] = 1
                    q.append(Point(nx, ny))
        if swed:
            cntOfSwed += 1
    if cntOfBlock == cntOfSwed:
        return 1
    else:
        return 0


if __name__ == '__main__':
    '''
    bfs模板题
    '''
    N = int(input())
    mark = [[0] * N for _ in range(N)]
    for _ in range(N):
        graph.append(input())
    for i in range(N):
        for j in range(N):
            if graph[i][j] == '#' and mark[i][j] == 0:
                ans += bfs(i, j)
    print(ans)

20 日志统计

提示:按时间排序,在时段内作id统计

'''
 标题:日志统计

 小明维护着一个程序员论坛。现在他收集了一份"点赞"日志,日志共有N行。其中每一行的格式是:

 ts id

 表示在ts时刻编号id的帖子收到一个"赞"。

 现在小明想统计有哪些帖子曾经是"热帖"。如果一个帖子曾在任意一个长度为D的时间段内收到不少于K个赞,小明就认为这个帖子曾是"热帖"。

 具体来说,如果存在某个时刻T满足该帖在[T, T+D)这段时间内(注意是左闭右开区间)收到不少于K个赞,该帖就曾是"热帖"。

 给定日志,请你帮助小明统计出所有曾是"热帖"的帖子编号。

 【输入格式】
 第一行包含三个整数N、D和K。
 以下N行每行一条日志,包含两个整数ts和id。

 对于50%的数据,1 <= K <= N <= 1000
 对于100%的数据,1 <= K <= N <= 100000 0 <= ts <= 100000 0 <= id <= 100000

 【输出格式】
 按从小到大的顺序输出热帖id。每个id一行。

 【输入样例】
7 10 2
0 1
0 10
10 10
10 1
9 1
100 3
100 3

 【输出样例】
 1
 3

 资源约定:
 峰值内存消耗(含虚拟机) < 256M
 CPU消耗  < 1000ms

'''


class record(object):
    def __init__(self, ts, td):
        self.ts = ts
        self.td = td

    def __eq__(self, other):
        return self.ts == other.ts

    def __lt__(self, other):
        return self.ts < other.ts

    def __str__(self):
        return f'{self.ts} {self.td}'


if __name__ == '__main__':
    import sys
    # sys.stdin = open('/Users/zhengwei/data/in8.txt')
    N, D, K = (int(x) for x in input().split(' '))
    records, ans = [], set()
    for i in range(N):
        ts, td = (int(x) for x in input().split(' '))
        records.append(record(ts, td))
    # 按照时间排序
    sorted_records = sorted(records)

    from collections import defaultdict

    id_cnt = defaultdict(int)
    j = 0
    for i in range(N):
        while j < N and sorted_records[j].ts - sorted_records[i].ts < D:
            j__td = sorted_records[j].td
            id_cnt[j__td] += 1  # 计数+1
            if id_cnt[j__td] >= K:
                ans.add(j__td)
            j += 1
        id_cnt[sorted_records[i].td] -= 1  # i的id的计数-1

    # 排序id并输出
    for id in sorted(ans):
        print(id)

21 等腰三角形

简单题

'''
标题:等腰三角形

本题目要求你在控制台输出一个由数字组成的等腰三角形。
具体的步骤是:
1. 先用1,2,3,...的自然数拼一个足够长的串
2. 用这个串填充三角形的三条边。从上方顶点开始,逆时针填充。
比如,当三角形高度是8时:

       1
      2 1
     3   8
    4     1
   5       7
  6         1
 7           6
891011121314151

显示不正确时,参看:p1.png

输入,一个正整数n(3

if __name__ == '__main__':
    n = int(input())
    # 用于填充三角形的字符的个数——字符串的长度
    length = 4 * (n - 1)
    # 递增数字和实际长度
    num, cur_len = 1, 0
    str_arr = []
    while cur_len < length:
        str1 = str(num)
        str_arr.append(str1)
        num += 1
        cur_len += len(str1)
    s = ''.join(str_arr)
    # 字符串构建完毕
    # 第一行
    for _ in range(n - 1):
        print('.', end='')
    print(s[0])
    # 中间行
    for i in range(1,n - 1):
        for j in range(n - i - 1):
            print('.', end='')
        print(s[i], end='')
        for j in range(2 * i - 1):
            print('.', end='')
        print(s[length - i])
    # 最后一行
    for i in range(n - 1, length - (n - 2)):
        print(s[i], end='')
    print()

你可能感兴趣的:(++蓝桥杯)