【Python刷题】Atcoder Beginner Contest

Atcoder Beginner Contest 371

A - Jiro

在这里插入图片描述

题目描述

有三个人,知道他们之中每两个人的年龄关系,输出年龄第二大的那个人是谁

算法思路

直接把所有情况列举即可

代码实现

s=list(input().split())
a=0
b=0
c=0
if s[0]=='<':
    if s[1]=='<' and s[2]=='<':
        print('B')
    elif s[1]=='<' and s[2]=='>':
        print('C')
    elif s[1]=='>':
        print('A')
elif s[0]=='>':
    if s[1]=='>' and s[2]=='>':
        print('B')
    elif s[1]=='>' and s[2]=='<':
        print('C')
    elif s[1]=='<':
        print('A')

B - Taro

在这里插入图片描述

题目描述

输入多组数据,每一组数据第一个值表示来自的家庭,第二个值表示出生孩子的性别,输入的数据已经按照出生时间来排序,对于每个家庭第一个出生的男孩输出Yes,其余输出No。

算法思路

用一个数组data来记录下每个家庭的第一个男孩是否出现即可。

代码实现

n,m=map(int, input().split())
data=[0]*(n+1)
for i in range(m):
    s=list(input().split())
    f=int(s[0])
    if s[1]=='M' and data[f]==0:
        print('Yes')
        data[f]=1
    else:
        print('No')

D - 1D Country

在这里插入图片描述

题目描述

有两个数组,每个数组对应的位置上分别记录村庄的位置以及村庄的人数。之后每次输入两个坐标然后输出这两个坐标之间的村庄的总人数

算法思路

前缀和预处理村庄人数,然后用bisect来找到左右两端点在村庄数组中的索引,最后计算出结果。

代码实现

from bisect import bisect_left, bisect_right
n = int(input())
x = list(map(int, input().split()))
p = list(map(int, input().split()))
qzh = [0] * (n + 1)
for i in range(n):
    qzh[i + 1] = qzh[i] + p[i]
q = int(input())
for _ in range(q):
    l, r = map(int, input().split())
    left_idx = bisect_left(x, l)
    right_idx = bisect_right(x, r)
    ans = qzh[right_idx] - qzh[left_idx]
    print(ans)

E - I Hate Sigma Problem

在这里插入图片描述

题目描述

输入n个数,计算出所有子序列中不重复元素的个数之和。

算法思路

这题如果用双重循环加set()来计算还是会超时。
于是用下面的算法:
把题目转换为求每一个元素给他所在的子序列贡献的值的和
假设是这样一组数
在这里插入图片描述
设输入的序列为data,我们用变量i作为data的下标从前往后遍历子集的起点,第一个数1为起点时他有下面这些子集。
在这里插入图片描述
设data的总长度为n不难发现以当前元素为起点时,子集的个数是 n + 1 − i n+1-i n+1i
这时候作为起点的1,给他所有子序列都贡献了一个唯一的数,于是答案加上6。
接着观察到第四个元素的时候,再次出现了元素1,如下图所示
在这里插入图片描述
蓝色表示的是以这个1为起点的子集,通过 n + 1 − i n+1-i n+1i可以算出有3个子序列,1为这三个子序列各贡献了一个唯一的元素。但是通过观察发现,他又为以2和3为起点的子序列(绿色和紫色)贡献了一个唯一的元素,而且绿色和紫色的子序列的个数分别与蓝色子序列的个数相同。于是我们可以得出下面的结论:
对于每一个数,先给以所有它为起点的子序列贡献1,然后找到当前位置和这个数上一次出现的位置之间的数,给这些数为起点且包含当前数的子序列贡献1。
于是推出下面的公式(设当前数上一次出现的位置为last,默认0)
每一个数的贡献为
( n − i + 1 ) ∗ ( i − l a s t ) (n-i+1)*(i-last) (ni+1)(ilast)

代码实现

n = int(input())
a = [0] * (n + 1)
b = [0] * (n + 1)
ans = 0
data=list(map(int, input().split()))
for i in range(1, n + 1):
    a[i] = int(data[i-1])
    ans += (n - i + 1) * (i - b[a[i]])
    b[a[i]] = i
print(ans)

Atcoder Beginner Contest 372

A - delete .

在这里插入图片描述

题目描述

把输入字符串中的.删掉

算法思路

用python自带的replace函数将.替换成空字符

代码实现

s = input()
print(s.replace(".", ""))

B - 3^A

在这里插入图片描述

题目描述

将一个数M拆成N项的和,每一项是3^A,其中
1 ≤ N ≤ 20 1\leq N \leq 20 1N20
0 ≤ A ≤ 10 0\leq A \leq 10 0A10
输出一个可能的结果,N,和一个存放A的序列

算法思路

每一次都考虑最大的情况,用while循环遍历所有的A,找到不超过M的最大的那一个,然后存放到结果序列中,并更新M的值,这样能使N尽可能的小,就不用担心超范围。

代码实现

n=int(input())
a=[]
def fun(n):
    i=0
    while 3**i<=n:
        i+=1
    return i-1
while n>0:
    num=fun(n)
    a.append(num)
    n-=3**num
print(len(a))
print(*a)

C - Count ABC Again

在这里插入图片描述

题目描述

每一次操作替换字符串中的一个字符,并输出当前字符串中有多少个‘ABC’子串

算法思路

由于改变一个字符只要考虑它左边两个和右边两个字符之内有没有新增ABC或减少ABC,这样省去了不必要的遍历,能大大提升时间复杂度。但是有一个很恶心的地方,那就是直接对字符串进行切片操作再使用count函数的效率不如把字符串转换成列表再操作。

代码实现

n, q = map(int, input().split())
s = list("##" + input() + "##") 
cnt = 0
for i in range(n + 2):
    if s[i:i + 3] == ['A', 'B', 'C']:
        cnt += 1
for _ in range(q):
    x, c = input().split()
    x = int(x) + 1 
    for i in range(x - 2, x + 1):
        if s[i:i + 3] == ['A', 'B', 'C']:
            cnt -= 1  
    s[x] = c  
    for i in range(x - 2, x + 1):
        if s[i:i + 3] == ['A', 'B', 'C']:
            cnt += 1 
    print(cnt)
    
##下面的代码超时    
# n,q=map(int, input().split())
# s="##"+input()+"##"
# cnt=s.count("ABC")
# for _ in range(q):
#     x,c=map(str,input().split())
#     x=int(x)+1
#     cnt-=s[x-2:x+3].count("ABC")
#     s=s[:x]+c+s[x+1:]
#     cnt+=s[x-2:x+3].count("ABC")
#     print(cnt)

D - Buildings

在这里插入图片描述

题目描述

已知数组H,对于每个i,输出满足H[i]~H[j]之间所有数都不超过H[j]的情况的j的个数

算法思路

观察发现,对于每一个i,满足条件的j所对应的元素H[j]都形成了一个递增的序列,所以可以从右往左遍历i,维护一个单调栈:当栈顶元素大于当前元素,则可以入栈,否则把栈中元素依次出栈知道栈顶元素小于当前元素。用一个变量count来记录满足条件的j的个数,如果当前元素H[i]使栈中的元素出栈了,出栈的那些元素被H[i]给挡住了,说明之后的所有情况的i,H[j]都不可能为那些元素,但是对于当前情况的i,H[j]是可以为这些出栈的元素的,因此每出栈一个元素,就给count加1。当出栈结束后,再将目前栈中元素个数加到count,因为他们构成了单调栈,是符合的,最后把H[i]入栈。

  • 由于理解起来比较抽象,可以举一个例子。
    假设当前的单调栈中元素为[3,5,9,10],假设目前的H[i]为8,要找到从i往后所有满足条件的j的个数。由于8入栈会破坏单调栈,因此要将栈顶小于8的元素依次出栈,但是发现H[j]可以取3,因为8和3之间没有元素会超过3,所以先将3出栈,count+=1。接着栈顶是5,而8到5之间只有一个3,也没有超过5的,因此5是满足条件的,count+=1。然后栈顶变成9,栈里没有比8小的元素了,剩下的两个数9和10都是符合条件的,count+=2,最后把8入栈。

代码实现

N = int(input())
H = list(map(int, input().split()))
result = [0] * N
stack = []
for i in range(N - 1, -1, -1):
    count = 0
    while stack and H[i] > H[stack[-1]]:
        stack.pop()
        count += 1
    count += len(stack)
    result[i] = count
    stack.append(i)
print(*result)

Atcoder Beginner Contest 374

A - Takahashi san 2

题目描述

输入字符串结尾为“san”输出“Yes”否则输出“No”

代码实现

s=input()
if s[-3:]=='san':
    print('Yes')
else:
    print('No')

B - Unvarnished Report

在这里插入图片描述

题目描述

输入两个字符串,如果完全一样输出0,否则输出第一个不一样的字母的位置

代码实现

a=input()
b=input()
if a==b:
    print(0)
else:
    i=0
    while i<min(len(a),len(b)) and a[i]==b[i]:
        i+=1
    print(i+1)

C - Separated Lunch

在这里插入图片描述

题目描述

将一个数组分成两组,每一组数分别求和,找一个分组方案使得两组求和后较大的数最小,并输出这个较大的数。

算法思路

这个问题可以转化成将一个数组分成两组并求和,求两个数最接近的情况。这道题可以使用二进制枚举的方法,通过枚举所有的分组情况,找到差值最小的那个方案。
二进制枚举
如果我们把这个数组的分组情况用1和0表示,1代表分到A组,0代表分到B组,那么就会形成一个由1和0组成的序列,把这个1和0的序列看作是一个数的二进制。假如说一共有5个数,不难发现所有的分组情况就包含在从00000到11111之间,用十进制来表示就是0到2^5-1之间。想要判断数组中的一个数是否被分到了A组,即在这条序列对应的位置是1,可以通过位运算来实现:
i&(1<<j)
i代表这个二进制序列的十进制表示,j代表数组的索引。如果计算结果返回1,说明数组中的第j个元素在当前的方案中被选中添加到A组。
当统计出加入A组中的元素并求和后,用总和与之相减就可以得到B组中的元素和了。然后在每个方案的最后更新答案就实现了。

代码实现

n=int(input())
k=list(map(int,input().split()))
ans=float('inf')
ks=sum(k)
for i in range(1<<n):
    a=0
    for j in range(n):
        if i&(1<<j):
            a+=k[j]
    ans=min(ans,max(a,ks-a))
print(ans)

Atcoder Beginner Contest 375

A - Seats

在这里插入图片描述

题目描述

输入一个包含#.的字符串,输出有多少个#.#子串

代码实现

n=int(input())
s=input()
ans=0
for i in range(0,n-2):
    if s[i:i+3]=="#.#":
        ans+=1
print(ans)

B - Traveling Takahashi Problem

在这里插入图片描述

题目描述

输入N坐标,从(0,0)点出发每次移动一个坐标,最后回到(0,0),求总路程

代码实现

import math
def distance(x1,y1,x2,y2):
    return math.sqrt((x2-x1)**2+(y2-y1)**2)
n=int(input())
total=0
x1,y1=0,0
for i in range(n):
    x2,y2=map(int,input().split())
    total+=distance(x1,y1,x2,y2)
    x1,y1=x2,y2
total+=distance(x1,y1,0,0)
print(f"{total:.20f}")

C - Spiral Rotation

在这里插入图片描述

题目描述

有一个包含#.的方阵,通过变换操作,每一次将方阵顺时针旋转90度然后将旋转的区域向内缩小一层
如图
在这里插入图片描述

算法思路

这个题目如果直接模拟的话一定会超时,旋转的时候因为所有应该旋转的点是同时旋转的,所以每次旋转都得临时建立一个副本,这样子会造成不必要的负担。我们可以只记录一下方阵初始时候的状态,然后根据当前旋转的层数变换初始方阵中的点,然后将变换后的点存到到答案方阵中,这样子就解决了所有点同时旋转的问题,具体看下面代码。

代码实现

N = int(input())
A = [list(input()) for _ in range(N)]
B = [[None]*N for _ in range(N)]
for i in range(N):
    for j in range(N):
        c = min(i + 1, j + 1, N - i, N - j)
        k = c % 4
        ni, nj = i, j
        for _ in range(k):
            ni, nj = nj, N - 1 - ni
        B[ni][nj] = A[i][j]
for row in B:
    print(''.join(row))

D - ABA

在这里插入图片描述

题目描述

输入一个只含有大写字母的字符串,从左到右选三个字符,不一定连续,但是能够组成回文串,求一共有多少种选法。

算法思路

不难想到,找到所有选法的个数其实就是在找每两个相同字符之间包含了多少字符,因此可以通过遍历每个字符,然后找到他后面所有与他相同的字符并给累加器加上这两个相同字符的索引之差-1,但是这个做法还是超时了。。下面是题解的做法,非常的巧妙。我们换一种思路,去遍历中间的那个数,那么对于每一个字符,就去寻找他左侧和右侧分别有多少相同字符,乘法原理可以很容易计算出,左右两侧相同字符的组合方法数量,可以用两个字典分别记录左边和右边的字符个数,初始化的时候遍历字符串一遍将结果存在右边的字典,之后每当遍历一个字符,就将他在右边字典中的个数减一,然后在左边字典中的个数加一。具体看代码。

代码实现

from collections import defaultdict
s = input()
l = defaultdict(int)# 用于保存当前位置左侧各个字母的个数
r = defaultdict(int)# 用于保存当前位置右侧侧各个字母的个数
for x in s: # 所有字符个数初始化记录在右侧
    r[x] += 1
ans = 0
for x in s:# 遍历字符串中的每个字符
    r[x] -= 1# 排除自身
    for c in l.keys():# 计算当前字符左右两侧相同字母的组合数量并加到总数中
        ans += l[c] * r[c]
    l[x] += 1# 将自身移到左侧
print(ans)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值