前言
是python组报名的基数太少了吗!!csdn上居然找不到ac的题解!只能自己钻研自己写了qwq
消除游戏
思路
这道题暴力能过75%的样例。
AC的数据结构是用双链表存字符串,因为用双链表删除一个字符的时间复杂度是 O ( 1 ) O(1) O(1)。其实是在暴力思路上做优化,暴力思路就是我每次都遍历字符串,找到当前所有的边缘字符,不断删除,直到字符串为空或者删除前后字符串没有发生变化为止。优化思路是,首先遍历一次字符串,找到边缘字符,然后只需要不断判断边缘字符左右2侧的字符是否为边缘字符,如果已经被判断为边缘字符过,就不要再重复判断了。这样做,第一次遍历时间复杂度是 O ( n ) O(n) O(n),后面找新的边缘字符时,每个字符最多被找一遍,也是 O ( n ) O(n) O(n) 的,所以整体时间复杂度是 O ( n ) O(n) O(n) 。
代码
蓝桥云课和Acwing上都能ac,但是new online judge上只能过80%的样例。
N = 1000010
l, r, st = [-1]*N, [-1]*N, [False]*N
edge = []
def remove(x):
r[l[x]] = r[x]; l[r[x]] = l[x]
def check(x): # 判断x是否为边缘字符
if s[l[x]]=='@' or s[r[x]]=='@': return
if s[x]==s[r[x]] and s[x]!=s[l[x]]:
edge.append(x); edge.append(l[x])
if s[x]==s[l[x]] and s[x]!=s[r[x]]:
edge.append(x); edge.append(r[x])
if __name__=='__main__':
s = input(); n = len(s); s = '@'+s+'@'
for i in range(1,n+1):
l[i] = i-1; r[i] = i+1
for i in range(1, n+1): check(i) # 初次检查边缘字符
i = 0
while i < len(edge): # 删除边缘字符 && 找新出现的边缘字符
pot = set()
while i<len(edge):
j = edge[i]
if st[j]==True: i+=1; continue
remove(j); st[j] = True
pot.add(l[j]); pot.add(r[j])
i+=1
for k in pot:
if st[k]==False: check(k)
p = False
for i in range(1, n+1):
if st[i]==False: print(s[i], end=''); p=True
if p==False: print('EMPTY')
# res = ''
# for i in range(1, n+1):
# if st[i]==False: res+=s[i]
# print('EMPTY' if res=='' else res)
技能升级
思路
解决此题有3重境界。
第一层境界,从题干里剥出核心问题。N个技能,每个技能可以提升的攻击力是成等差数列递减的,问最多可以提升多少攻击力,那么一定是从所有数里面选最大的m个。
第二层境界,用最大堆去维护所有数,每次从堆里删除一个数的时间复杂度是 O ( l o g n ) O(logn) O(logn),选出m个数的时间复杂度是 O ( m l o g n ) O(mlogn) O(mlogn)。python中可以用heapeq模块来模拟最小堆,把每个数取相反数存进堆中,就可以实现最大堆。
第三层境界,比较奇葩。我们最终的目的是在所有数中挑出前m个最大的数,那么可以用二分找出第m个大的数 x x x 。二分的范围是 [ 0 , 1 0 6 ] [0, 10^6] [0,106],并且此序列具有二段性,一边的区间 > = x >=x >=x 的数 < m <m <m 个,另一边的区间 > = x >=x >=x 的数 > = m >=m >=m 个,二分的时间复杂度是 O ( l o g n ) O(logn) O(logn)。接下来找出每个技能中, > = x >=x >=x 的数的个数。由于每个技能的数都是单调递减的等差数列,可以用等差数列的公式求得个数 ⌊ ( a i − x ) b i ⌋ + 1 \lfloor\frac{(a_i-x)}{b_i}\rfloor+1 ⌊bi(ai−x)⌋+1。计算每个技能提升的攻击力总和,也只需要用等差数列求和公式 ( a 1 + a n ) n 2 \frac{(a_1+a_n)n}{2} 2(a1+an)n 即可。
代码
一:绝对暴力
acwing上通过9/11样例,new online judge上过10%,蓝桥云课过40%。
N = 100010
if __name__=='__main__':
a, b = [], []
n, m = map(int, input().split())
for _ in range(n):
aa, bb = map(int, input().split())
a.append(aa); b.append(bb)
nums = []
for i in range(n):
aa, bb = a[i], b[i]
while aa>0:
nums.append(aa)
aa -= bb
nums.sort(reverse = True) # 降序
res = 0
for i in range(min(m, len(nums))):
res += nums[i]
print(res)
二:堆优化
acwing上通过7/12样例。new online judge居然内存超限,过10%。,蓝桥云课过40%。
import heapq
N = 100010
if __name__=='__main__':
a, b = [], []
n, m = map(int, input().split())
for _ in range(n):
aa, bb = map(int, input().split())
a.append(aa); b.append(bb)
nums = []
for i in range(n):
aa, bb = a[i], b[i]
while aa>0:
heapq.heappush(nums, -aa)
aa -= bb
res = 0
for i in range(min(m, len(nums))):
res += -heapq.heappop(nums)
print(res)
三:二分
AC!!
N = 100010
def check(mid):
global m
cnt = 0
for i in range(n):
if a[i]>=mid: cnt += (a[i]-mid)//b[i]+1
return cnt>=m
if __name__=='__main__':
global m
a, b = [], []
n, m = map(int, input().split())
for _ in range(n):
aa, bb = map(int, input().split())
a.append(aa); b.append(bb)
# 二分找分界点
l, r =0, 1000010
while l<r:
mid = (l+r+1)//2
if check(mid): l=mid
else: r=mid-1
# 计算>=x的数的总和/个数
count, sum = 0, 0
for i in range(n):
if a[i]>=r:
c = (a[i]-r)//b[i]+1
end = a[i]-(c-1)*b[i]
count += c
sum += (a[i]+end)*c//2
print(sum - (count-m)*r)
后记
期末考试跟国赛撞了,后面没怎么写了。现在国赛也考完了。以后有时间再填坑。如果对大家有帮助的话,欢迎点赞收藏~!