【蓝桥杯python研究生组备赛】002 枚举+模拟+排序

题目1:连号区间数

[蓝桥杯 2013 省 B] 连号区间数

题目描述

小明这些天一直在思考这样一个奇怪而有趣的问题:

1 1 1 ~ N N N 的某个全排列中有多少个连号区间呢?这里所说的连号区间的定义是:

如果区间 [ L , R ] [L, R] [L,R] 里的所有元素(即此排列的第 L L L个到第 R R R 个元素)递增排序后能得到一个长度为 R − L + 1 R-L+1 RL+1 的“连续”数列,则称这个区间连号区间。

N N N 很小的时候,小明可以很快地算出答案,但是当 N N N 变大的时候,问题就不是那么简单了,现在小明需要你的帮助。

输入格式

第一行是一个正整数 N ( 1 ≤ N ≤ 50000 ) N (1 \le N \le 50000) N(1N50000), 表示全排列的规模。

第二行是 N N N 个不同的数字 P i ( 1 ≤ P i ≤ N ) P_i(1 \le P_i \le N) Pi(1PiN), 表示这 N N N 个数字的某一全排列。

输出格式

输出一个整数,表示不同连号区间的数目。

输入输出样例

输入

4
3 2 4 1

输出

7

输入输出样例 #2

输入 #2

5
3 4 2 5 1

输出 #2

9

说明/提示

第一个用例中,有 7 7 7 个连号区间分别是: [ 1 , 1 ] [1,1] [1,1], [ 1 , 2 ] [1,2] [1,2], [ 1 , 3 ] [1,3] [1,3], [ 1 , 4 ] [1,4] [1,4], [ 2 , 2 ] [2,2] [2,2], [ 3 , 3 ] [3,3] [3,3], [ 4 , 4 ] [4,4] [4,4]

第二个用例中,有 9 9 9 个连号区间分别是: [ 1 , 1 ] [1,1] [1,1], [ 1 , 2 ] [1,2] [1,2], [ 1 , 3 ] [1,3] [1,3], [ 1 , 4 ] [1,4] [1,4], [ 1 , 5 ] [1,5] [1,5], [ 2 , 2 ] [2,2] [2,2], [ 3 , 3 ] [3,3] [3,3], [ 4 , 4 ] [4,4] [4,4], [ 5 , 5 ] [5,5] [5,5]

原题时限 5 秒, 64M。蓝桥杯 2013 年第四届省赛

python代码

n=int(input())

data=list(map(int,input().split()))

ans=0
for i in range(n):
    max1=float('-inf')
    min1=float('inf')
    for j in range(i,n,1):
        max1=max(max1,data[j])
        min1=min(min1,data[j])
        if max1-min1==(j-i):
            ans+=1
print(ans)

知识点

  1. 全排列,不涉及到重复元素的问题
  2. 那么找到区间内的最大值与最小值,如果最大值-最小值==r-l,符合条件
  3. float('inf'):正无穷大; float('-inf'):负无穷大

题目2:递增三元组

[蓝桥杯 2018 省 B] 递增三元组

题目描述

给定三个整数数组 A = [ A 1 , A 2 , ⋯   , A N ] A = [A_1, A_2,\cdots, A_N] A=[A1,A2,,AN] B = [ B 1 , B 2 , ⋯   , B N ] B = [B_1, B_2,\cdots, B_N] B=[B1,B2,,BN] C = [ C 1 , C 2 , ⋯   , C N ] C = [C_1, C_2,\cdots,C_N] C=[C1,C2,,CN]

请你统计有多少个三元组 ( i , j , k ) (i, j, k) (i,j,k) 满足:

  1. 1 ≤ i , j , k ≤ N 1 \le i, j, k \le N 1i,j,kN
  2. A i < B j < C k A_i < B_j < C_k Ai<Bj<Ck

输入格式

第一行包含一个整数 N N N

第二行包含 N N N 个整数 A 1 , A 2 , ⋯   , A N A_1, A_2,\cdots, A_N A1,A2,,AN

第三行包含 N N N 个整数 B 1 , B 2 , ⋯   , B N B_1, B_2,\cdots, B_N B1,B2,,BN

第四行包含 N N N 个整数 C 1 , C 2 , ⋯   , C N C_1, C_2,\cdots, C_N C1,C2,,CN

输出格式

一个整数表示答案

输入输出样例

输入

3
1 1 1
2 2 2
3 3 3

输出

27

说明/提示

对于 30 % 30\% 30% 的数据, 1 ≤ N ≤ 100 1 \le N \le 100 1N100

对于 60 % 60\% 60% 的数据, 1 ≤ N ≤ 1000 1 \le N \le 1000 1N1000

对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 1 0 5 1 \le N \le 10^5 1N105 0 ≤ A i , B i , C i ≤ 1 0 5 0 \le A_i, B_i, C_i \le 10^5 0Ai,Bi,Ci105

python代码

n=int(input())
N=10**5+10
A=list(map(int,input().split()))
B=list(map(int,input().split()))
C=list(map(int,input().split()))
#每一个元素都加一,方便前缀和下标从1开始,且不影响前后大小关系
A=[i+1 for  i in A]
B=[i+1 for  i in B]
C=[i+1 for  i in C]


ass=[0]*N
css=[0]*N

#计算A
cnt1=[0]*N#某一个值出现的次数
S1=[0]*N
for i in range(n):
    cnt1[A[i]]+=1

for i in range(1,N):#前缀和
    S1[i]=S1[i-1]+cnt1[i]

for i in range(n):
    ass[i]=S1[B[i]-1]    

#计算C
cnt2=[0]*N#某一个值出现的次数
S2=[0]*N
for i in range(n):
    cnt2[C[i]]+=1

for i in range(1,N):#前缀和
    S2[i]=S2[i-1]+cnt2[i]

for i in range(n):
    css[i]=S2[N-1]-S2[B[i]]  
ans=0   
for i in range(n):
    ans+=(ass[i]*css[i])
# print(cnt1[0:10])
# print(S1[0:10])
# print(ass[0:5])
# print(css[0:5])

print(ans)
    

知识点

  1. 首先根据数据范围 1 0 5 10^5 105可知,最多使用一个循环,那么循环哪一组呢?A与C地位是等价的,假设遍历A中元素,那么只能做到 B j > A i , C j > A i B_j>A_i,C_j>A_i Bj>Ai,Cj>Ai,依然无法确定唯一的顺序,故只能遍历B中元素
  2. 下一步是利用什么方法?可以用前缀和 or 排序+二分方法,以前缀和为例
  3. 对于A列表,先开一个N长度的列表cnt1, c n t 1 [ A [ i ] ] cnt1[A[i]] cnt1[A[i]]统计A中每一个元素出现的次数,那么S1前缀和的含义是? S 1 [ A i ] 含义就是小于等于 A i 的个数 S1[A_i]含义就是小于等于A_i的个数 S1[Ai]含义就是小于等于Ai的个数
  4. 同样地,对于C列表,大于 B j B_j Bj的个数就是 S 2 [ N − 1 ] − S 2 [ B j ] S2[N-1]-S2[B_j] S2[N1]S2[Bj]
  5. 那么最后的方案数目就是 ass[i]*css[i]的累加

题目3:封闭图形个数

[蓝桥杯 2024 省 C] 封闭图形个数

题目描述

在蓝桥王国,数字的大小不仅仅取决于它们的数值大小,还取决于它们所形成的“封闭图形”的个数。

封闭图形是指数字中完全封闭的空间,例如数字 1 1 1 2 2 2 3 3 3 5 5 5 7 7 7 都没有形成封闭图形,而数字 0 0 0 4 4 4 6 6 6 9 9 9 分别形成了 1 1 1 个封闭图形,数字 8 8 8 则形成了 2 2 2 个封闭图形。值得注意的是,封闭图形的个数是可以累加的。例如,对于数字 68 68 68,由于 6 6 6 形成了 1 1 1 个封闭图形,而 8 8 8 形成了 2 2 2 个,所以 68 68 68 形成的封闭图形的个数总共为 3 3 3

在比较两个数的大小时,如果它们的封闭图形个数不同,那么封闭图形个数较多的数更大。例如,数字 41 41 41 和数字 18 18 18,它们对应的封闭图形的个数分别为 1 1 1 2 2 2,因此数字 41 41 41 小于数组 18 18 18。如果两个数的封闭图形个数相同,那么数值较大的数更大。例如,数字 14 14 14 和数字 41 41 41,它们的封闭图形的个数都是 1 1 1,但 14 < 41 14 < 41 14<41,所以数字 14 14 14 小于数字 41 41 41。如果两个数字的封闭图形个数和数值都相同,那么这两个数字被认为是相等的。

小蓝对蓝桥王国的数字大小规则十分感兴趣。现在,他将给定你 n n n 个数 a 1 , a 2 , ⋯   , a n a_1, a_2,\cdots, a_n a1,a2,,an,请你按照蓝桥王国的数字大小规则,将这 n n n 数从小到大排序,并输出排序后结果。

输入格式

输入的第一行包含一个整数 n n n,表示给定的数字个数。

第二行包含 n n n 个整数 a 1 , a 2 , ⋯   , a n a_1, a_2,\cdots, a_n a1,a2,,an,相邻整数之间使用一个空格分隔,表示待排序的数字。

输出格式

输出一行包含 n n n 个整数,相邻整数之间使用一个空格分隔,表示按照蓝桥王国的数字大小规则从小到大排序后的结果。

输入输出样例 #1

输入 #1

3
18 29 6

输出 #1

6 29 18

说明/提示

【样例说明】

对于给定的数字序列 [ 18 , 29 , 6 ] [18, 29, 6] [18,29,6],数字 18 18 18 的封闭图形个数为 2 2 2,数字 29 29 29 的封闭图形个数为 1 1 1,数字 6 6 6 的封闭图形个数为 1 1 1。按照封闭图形个数从小到大排序后,得到 [ 29 , 6 , 18 ] [29, 6, 18] [29,6,18]

由于数字 29 29 29 和数字 6 6 6 的封闭图形个数相同,因此需要进一步按照数值大小对它们进行排序,最终得到 [ 6 , 29 , 18 ] [6, 29, 18] [6,29,18]

【评测用例规模与约定】

对于 50 % 50\% 50% 的评测用例, 1 ≤ n ≤ 2 × 1 0 3 1\le n \le 2 \times 10^3 1n2×103 1 ≤ a i ≤ 1 0 5 1 \le a_i \le 10^5 1ai105
对于所有评测用例, 1 ≤ n ≤ 2 × 1 0 5 1 \le n\le 2 \times 10^5 1n2×105 1 ≤ a i ≤ 1 0 9 1 \le a_i \le 10^9 1ai109

python代码

n=int(input())
data=list(map(int,input().split()))

lq=[0]*n

for i in range(n):
    a=str(data[i])
    sum1=sum2=0#统计数字
    sum1=a.count('0')+a.count('4')+a.count('6')+a.count('9')
    sum2=a.count('8')*2
    lq[i]=sum1+sum2

#首先按照新的规则,计算每一个数字的“大小”,记为lq[i],然后用元组(data[i],lq[i])记录所有数据
combined_data=[(data[i],lq[i])for i in range(n)]

combined_data.sort(key=lambda x:(x[1],x[0]))
answer=[]
for i in range(n):
    answer.append(combined_data[i][0])  
print(*answer)


知识点

  1. 主要考察排序sort函数
  2. 首先按照新的规则,计算每一个数字的“大小”,记为lq[i],然后用元组(data[i],lq[i])记录所有数据
  3. 排序,sort(*, key=None, reverse=False),只接受两个参数,在这里会先按照新规则排序,如果两个元素的新规则大小一样,则按照原数值排序
  4. 之后提取数据,并输出,print(*answer)==print(*answer,sep=' '):默认按照空格 连接元素

题目4: 回文日期

[NOIP 2016 普及组] 回文日期

题目背景

NOIP2016 普及组 T2

题目描述

在日常生活中,通过年、月、日这三个要素可以表示出一个唯一确定的日期。

牛牛习惯用 8 8 8 位数字表示一个日期,其中,前 4 4 4 位代表年份,接下来 2 2 2 位代表月份,最后 2 2 2 位代表日期。显然:一个日期只有一种表示方法,而两个不同的日期的表 示方法不会相同。

牛牛认为,一个日期是回文的,当且仅当表示这个日期的 8 8 8 位数字是回文的。现在,牛牛想知道:在他指定的两个日期之间包含这两个日期本身),有多少个真实存在的日期是回文的。

一个 8 8 8 位数字是回文的,当且仅当对于所有的 i i i 1 ≤ i ≤ 8 1 \le i \le 8 1i8)从左向右数的第 i i i 个数字和第 9 − i 9-i 9i 个数字(即从右向左数的第 i i i 个数字)是相同的。

例如:

  • 对于 2016 年 11 月 19 日,用 8 8 8 位数字 20161119 20161119 20161119 表示,它不是回文的。
  • 对于 2010 年 1 月 2 日,用 8 8 8 位数字 20100102 20100102 20100102 表示,它是回文的。
  • 对于 2010 年 10 月 2 日,用 8 8 8 位数字 20101002 20101002 20101002 表示,它不是回文的。

每一年中都有 12 12 12 个月份:

其中, 1 , 3 , 5 , 7 , 8 , 10 , 12 1, 3, 5, 7, 8, 10, 12 1,3,5,7,8,10,12 月每个月有 31 31 31 天; 4 , 6 , 9 , 11 4, 6, 9, 11 4,6,9,11 月每个月有 30 30 30 天;而对于 2 2 2 月,闰年时有 29 29 29 天,平年时有 28 28 28 天。

一个年份是闰年当且仅当它满足下列两种情况其中的一种:

  1. 这个年份是 4 4 4 的整数倍,但不是 100 100 100 的整数倍;
  2. 这个年份是 400 400 400 的整数倍。

例如:

  • 以下几个年份都是闰年: 2000 , 2012 , 2016 2000, 2012, 2016 2000,2012,2016
  • 以下几个年份是平年: 1900 , 2011 , 2014 1900, 2011, 2014 1900,2011,2014

输入格式

两行,每行包括一个 8 8 8 位数字。

第一行表示牛牛指定的起始日期。

第二行表示牛牛指定的终止日期。

保证 d a t e 1 \mathit{date}_1 date1 d a t e 2 \mathit{date}_2 date2 都是真实存在的日期,且年份部分一定为 4 4 4 位数字,且首位数字不为 0 0 0

保证 d a t e 1 \mathit{date}_1 date1 一定不晚于 d a t e 2 \mathit{date}_2 date2

输出格式

一个整数,表示在 d a t e 1 \mathit{date}_1 date1 d a t e 2 \mathit{date}_2 date2 之间,有多少个日期是回文的。

输入输出样例 #1

输入 #1

20110101
20111231

输出 #1

1

输入输出样例 #2

输入 #2

20000101
20101231

输出 #2

2

说明/提示

【样例说明】

对于样例 1,符合条件的日期是 20111102 20111102 20111102

对于样例 2,符合条件的日期是 20011002 20011002 20011002 20100102 20100102 20100102

【子任务】

对于 60 % 60 \% 60% 的数据,满足 d a t e 1 = d a t e 2 \mathit{date}_1 = \mathit{date}_2 date1=date2

python代码

start=input()
end=input()

a=int(start)
b=int(end)

ans=0
days=[0,31,28,31,30,31,30,31,31,30,31,30,31]
def check(date):
    year=date//10000
    month=date%10000//100
    day=date%100
    if (year%4==0 and year%100!=0) or year%400==0:#判断是否是闰年
        leap=1
    else:
        leap=0
    if month==0 or month >12:return False
    if day==0 or (month!=2 and day>days[month]):return False
    if month==2:
        if day >28+leap:return False
    return True


for i in range(1000,10000):#逆向思维,先枚举回文数,前四位,然后判断是否在范围内,再判断日期是否合法
    date=i
    x=i
    for j in range(4):
        date=date*10+x%10
        x//=10
    if date>=a and date<=b and check(date):
        ans+=1
        
         
print(ans)

知识点

  1. 正向的思维是,遍历日期,然后判断是否是回文数组,但是 1 0 8 10^8 108会超时
  2. 逆向思维是,遍历前四位数,这时后四位数已经确定该回文数,然后判断:
    - 是否在 [ d a t e 1 , d a t e 2 ] [date_1,date_2] [date1,date2]
    - 是否是合法日期
  3. 抠出来date的每一位,
    while (date):
       x=date%10
       date//=10
       print(x) 
    
  4. 判断闰年,year%4==0 and year%4!=0 or year%400==0
  5. 更多细节知识点,
    蓝桥杯python组备赛笔记(超详细版)

题目5:移动距离

X星球居民小区的楼房全是一样的,并且按矩阵样式排列。

其楼房的编号为 1,2,3…

当排满一行时,从下一行相邻的楼往反方向排号。

比如:当小区排号宽度为 6 时,开始情形如下:

1  2  3  4  5  6
12 11 10 9  8  7
13 14 15 .....

我们的问题是:已知了两个楼号 m 和 n,需要求出它们之间的最短移动距离(不能斜线方向移动)。

输入格式

输入共一行,包含三个整数 w,m,n,w 为排号宽度,m,n 为待计算的楼号。

输出格式

输出一个整数,表示 m,n 两楼间最短移动距离。

数据范围

1≤w,m,n≤10000,

输入样例:
6 8 2
输出样例:
4

python代码

w,m,n=map(int,input().split())
m-=1
n-=1
ans=0

a,b=divmod(m,w)
c,d=divmod(n,w)

#第一个位置坐标为(0,0)
if a%2!=0:#奇数行需要更改顺序
    b=w-b-1
    
if c%2!=0:#奇数行需要更改顺序
    d=w-d-1
ans=abs(c-a)+abs(d-b)

print(ans)

知识点

  1. 根据元素在矩阵的坐标来得到答案,第一个位置坐标为(0,0)
  2. 偶数行是正常的顺序,只有奇数行逆序,因此只需要对奇数行进行处理

题目6:日期问题

小明正在整理一批历史文献。这些历史文献中出现了很多日期。

小明知道这些日期都在1960年1月1日至2059年12月31日。

令小明头疼的是,这些日期采用的格式非常不统一,有采用年/月/日的,有采用月/日/年的,还有采用日/月/年的。

更加麻烦的是,年份也都省略了前两位,使得文献上的一个日期,存在很多可能的日期与其对应。

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

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

输入格式

一个日期,格式是”AA/BB/CC”。

即每个’/’隔开的部分由两个 0-9 之间的数字(不一定相同)组成。

输出格式

输出若干个不相同的日期,每个日期一行,格式是”yyyy-MM-dd”。

多个日期按从早到晚排列。

数据范围

0≤A,B,C≤9

输入样例:
02/03/04
输出样例:
2002-03-04
2004-02-03
2004-03-02

python代码

def check(year,month,day):
    days=[0,31,28,31,30,31,30,31,31,30,31,30,31]
    if month>12 or month==0:
        return False
    if day ==0 or (month!=2 and day>days[month]):return False
    if (year%4==0 and year%100!=0) or year%400==0:
        leap=1
    else:
        leap=0
    if month==2:
        if day>28+leap:return False
    return True
    



if __name__=="__main__":
    
    a,b,c=map(int,input().split('/'))
    for i in range(19600101,20591231):
        s=str(i)
        year=int(s[2:4])
        month=int(s[4:6])
        day=int(s[6:])
        if check(year,month,day):#判断日期是否合法
            if (year==a and month==b and day== c) or (month==a and day==b and year== c) or (day==a and month==b and year== c):
                ans=s[:4]+'-'+s[4:6]+'-'+s[6:]
                print(ans)
            
    

知识点

  1. 一共只有100年,100*365,数据规模支持遍历每一天
  2. 正向思维是,排列组合00~99,然后判断日期是否合法,最后需要进行排序
  3. 逆向思维是,遍历数字,然后截取年/月/日的,或月/日/年的,或日/月/年,是否与输入 相等,之后判断日期是否合法
  4. 主要考察 日期是否合法

题目7:航班时间

小 h 前往美国参加了蓝桥杯国际赛。

小 h 的女朋友发现小 h 上午十点出发,上午十二点到达美国,于是感叹到“现在飞机飞得真快,两小时就能到美国了”。

小 h 对超音速飞行感到十分恐惧。

仔细观察后发现飞机的起降时间都是当地时间。

由于北京和美国东部有 12 小时时差,故飞机总共需要 14 小时的飞行时间。

不久后小 h 的女朋友去中东交换。

小 h 并不知道中东与北京的时差。

但是小 h 得到了女朋友来回航班的起降时间。

小 h 想知道女朋友的航班飞行时间是多少。

对于一个可能跨时区的航班,给定来回程的起降时间。

假设飞机来回飞行时间相同,求飞机的飞行时间。

输入格式

一个输入包含多组数据。

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

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

起降时间的格式如下:

  1. h1:m1:s1 h2:m2:s2
  2. h1:m1:s1 h3:m3:s3 (+1)
  3. h1:m1:s1 h4:m4:s4 (+2)

第一种格式表示该航班在当地时间h1时m1分s1秒起飞,在当地时间当日h2时m2分s2秒降落。

第二种格式表示该航班在当地时间h1时m1分s1秒起飞,在当地时间次日h2时m2分s2秒降落。

第三种格式表示该航班在当地时间h1时m1分s1秒起飞,在当地时间第三日h2时m2分s2秒降落。

输出格式

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

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

数据范围

保证输入时间合法(0≤h≤23,0≤m,s≤59),飞行时间不超过24小时。

输入样例:

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

python代码

#字符串处理

n=int(input())
for i in range(n):
    a=input()
    if a[-1]!=')':#没有 到第二天
        h1=int(a[0:2])
        m1=int(a[3:5])
        s1=int(a[6:8])
        seconds1=h1*3600+m1*60+s1
        
        h2=int(a[9:11])
        m2=int(a[12:14])
        s2=int(a[15:16])
        seconds2=h2*3600+m2*60+s2
    
        
    else:#存在第二天,甚至到了第三天
        
        h1=int(a[0:2])
        m1=int(a[3:5])
        s1=int(a[6:8])
        seconds1=h1*3600+m1*60+s1
        
        h2=int(a[9:11])
        m2=int(a[12:14])
        s2=int(a[15:16])
        seconds2=h2*3600+m2*60+s2
        
        day=int(a[20])
        seconds2+=day*24*3600#最终的秒数
        
    b=input()
    if b[-1]!=')':#没有 到第二天
        h3=int(b[0:2])
        m3=int(b[3:5])
        s3=int(b[6:8])
        seconds3=h3*3600+m3*60+s3
        
        h4=int(b[9:11])
        m4=int(b[12:14])
        s4=int(b[15:16])
        seconds4=h4*3600+m4*60+s4
    
        
    else:#存在第二天,甚至到了第三天
        
        h3=int(b[0:2])
        m3=int(b[3:5])
        s1=int(b[6:8])
        seconds3=h3*3600+m3*60+s3
        
        h2=int(b[9:11])
        m2=int(b[12:14])
        s2=int(b[15:16])
        seconds4=h4*3600+m4*60+s4
        
        day2=int(b[20])
        seconds4+=day2*24*3600#最终的秒数
    
    
    
        
    ans=((seconds2-seconds1)+(seconds4-seconds3))//2
    #格式化处理日期
    hour=ans//3600
    minute=ans//60-hour*60
    seconds=ans%60
    
    print(f'{hour:>02d}:{minute:>02d}:{seconds:>02d}')    
    
    

知识点

  1. 主要考察字符串的处理,把时间都化成秒来计算
  2. 飞行时间=((到达时间-出发时间)+时差+(到达时间-出发时间)-时差)//2
  3. 当最后存在(+1)或(+2),把相应的秒数加上
  4. 格式化输出日期,>表示右对齐,左面不足的补0,'2'表示宽度,d表示十进制整数

题目8:外卖店优先级

“饱了么”外卖系统中维护着 N 家外卖店,编号 1∼N。

每家外卖店都有一个优先级,初始时 (0 时刻) 优先级都为 0。

每经过 1 个时间单位,如果外卖店没有订单,则优先级会减少 1,最低减到 0;而如果外卖店有订单,则优先级不减反加,每有一单优先级加 2。

如果某家外卖店某时刻优先级大于 5,则会被系统加入优先缓存中;如果优先级小于等于 3,则会被清除出优先缓存。

给定 T 时刻以内的 M 条订单信息,请你计算 T 时刻时有多少外卖店在优先缓存中。

输入格式

第一行包含 3 个整数 N,M,T。

以下 M 行每行包含两个整数 ts 和 id,表示 ts 时刻编号 id 的外卖店收到一个订单。

输出格式

输出一个整数代表答案。

数据范围

1≤N,M,T≤105,
1≤ts≤T,
1≤id≤N

输入样例:
2 6 6
1 1
5 2
3 1
6 2
2 1
6 2
输出样例:
1
样例解释

6 时刻时,1 号店优先级降到 3,被移除出优先缓存;2 号店优先级升到 6,加入优先缓存。

所以是有 1 家店 (2 号) 在优先缓存中。

思路
  1. 按照id 排序
  2. id相同,则按照时间排序
  3. 计算有订单的最后一个时刻time1,保存下来,以及它的id
  4. 合并没有订单的区间,即处理time1之前的数据
  5. 处理time1时刻数据,优先级+2*cnt
  6. 如果time1<t,再处理最后一段 没有订单的区间
python代码
n,m,t=map(int,input().split())
data=[]

for i in range(m):#输入数据
    a,b=map(int,input().split())
    data.append((a,b))

data.sort(key=lambda x:(x[1],x[0]))

score=[0]*(n+1)#第i个店铺的优先级,从1开始
last=[0]*(n+1)#第i个店铺上一次有订单的时刻
st=[False]*(n+1)#是否处于优先缓冲区,True表示处于

# print(data)
i=0
while i<m:#一次处理一批数据,id和时刻都相同
    j=i
    while j>=0 and j<m and (data[j][0]==data[i][0]):#处理同一时刻,同一id有订单的区间
        j+=1
    # print(j)
    time1=data[i][0]
    id=data[i][1]
    cnt=j-i
    
    # print(time1,id,cnt)

    score[id]-=(time1-last[id]-1)#没有订单
    if score[id]<0:
        score[id]=0
    if score[id]<=3:#移出来 优先缓冲区
        st[id]=False
    
    score[id]+=cnt*2#处理有订单的这一时刻
    
    if score[id]>5:#优先缓冲区
        st[id]=True
    i=j
    last[id]=time1
for i in range(1,n+1):
    
    if last[i]<t:#处理最后的一段,没有订单的
        score[i]-=t-last[i]#没有订单
            
        if score[i]<=3:#移出来 优先缓冲区
            st[i]=False

ans=0
for i in range(1,n+1):
    if st[i]==True:
        ans+=1

print(ans)    
    

知识点

  1. data.sort(key=lambda x:(x[1],x[0])):对data数据进行排序,先按照每个元素的第2个值,即店铺的id排序,若id相同,按照元素的第一个值即时间排序

更多细节知识点,
蓝桥杯python组备赛笔记(超详细版)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值