需要用到前缀和与差分两种思路的题目(python)


这些题目,都是差分加前缀和再加上一些其他的知识点,其他知识点千变万化,这类题目需要多加训练才能一眼看出需要用到差分与前缀和的思想。由于差分与前缀和已经在前面讲过,所以下面的题目只会在程序中简单解释。

题目链接

  1. 阿坤老师的课堂挑战
  2. 大衣的最大排序和
  3. 充能计划
  4. 大石头的搬运工
  5. 推箱子

1. 阿坤老师的课堂挑战

代码如下:

n,q = map(int,input().split())
a = [0]*(n+2)
b = []
#  求差分
for _ in range(q):
  l,r,x = map(int,input().split())
  a[l]+=x
  a[r+1]-=x
  b.append([l,r,x])
#   求前缀和
for i in range(1,n+1):
  a[i]+=a[i-1]
# 找出最大值
maxv = max(a)
# 找出下标
p = a.index(maxv)
maxx = 0
# 判断最大值是否在边界里
for l,r,x in b:
  if l<=p<=r:
    maxx=max(maxx,x)
print(maxv-maxx)

2. 大衣的最大排序和

n,q = map(int,input().split())
a = list(map(int,input().split()))
a.sort(reverse=True)
b = [0]*(n+2)
# 差分前缀和来存a的第i个位置被加了几次
# 差分
for _ in range(q):
  l,r = map(int,input().split())
  b[l] += 1
  b[r+1] -= 1
# 前缀和
for i in range(1,n+1):
  b[i] += b[i-1]
  # v是位置,p是值
c = [(v,p)for v,p in enumerate(b[1:-1],1)]
# 按值与字典值逆序排序
# 题目说按字典值正序排序,但此处按逆序是因为a列表是逆序,这样通过下面赋值的操作就能使最后的结果为正序
c.sort(key=lambda x:(-x[1],-x[0]))
# ans记录最大值
ans =0
# res记录最小字典序
res = [0]*n
for x,(v,p) in zip(a,c):
  ans += x*p
  # 这里p-1是因为前面enumerate从1开始记录下标
  res[v-1] = x
print(ans)
print(*res)

3. 充能计划

n, m, q = map(int, input().split())
s = [0] + list(map(int, input().split()))
lst = [list(map(int, input().split())) for _ in range(q)]
# 可以将充能的列表排序,这样可以用每个种类的宝石不断从当前位置往后充能,能够避免同一类宝石重复充能某个位置
lst.sort()
print(lst)
diff = [0] * (n+2) # 差分数组
idx = 0; ind = 0 # 当前何种能量 处在何处
for p, k in lst:
    if p != idx: # 开始下一个能量的计算
        idx = p; ind = k - 1
    if ind >= k + s[p] - 1 or ind >= n: # 能量相当于没加
        continue
    left = max(ind+1, k); right = min(k + s[p] - 1, n)
    # 将[left, right]整体加1
    diff[left] += 1; diff[right+1] -= 1
    ind = right
# 前缀和
score = 0
for i in range(1, n+1):
    score += diff[i]
    print(score, end = " ")

4. 推箱子

这题也是要用到前缀和的,但是与之前的不太一样,放在这主要是为下题做准备
推箱子代码如下:
思路:以中间箱子(数量中间数)为基准,左边箱子向右移动,右边箱子向左移动

# 前两句为批量读取数据
from sys import stdin
input = stdin.readline
# t个关卡
t = int(input().strip())
for _ in range(t):
	# n表示可用位子情况
    s = int(input().strip())
    # a表示去除前后空位置之后的箱子
    # 因为前后空位置对移动箱子的次数没有影响
    a = input().strip().strip("_")
    # n表示有几个箱子与空位
    n = len(a)
    # m表示有几个箱子
    m = a.count("@")
    # 计数,计数有几个箱子,方便找箱子的中位值
    cnt = 0
    # 找箱子的中位值
    for i, c in enumerate(a):
        if c == "@":
            cnt += 1
        if cnt == (m + 1) // 2:
            mid = i
            break
    # ans表示共需要移动几步
    ans = 0
    # 移动遇到的空格数
    space = 0
    # 左边箱子向右移动
    for i in range(mid - 1, -1, -1):
        if a[i] == "_":
            space += 1
        if a[i] == "@":
            ans += space
    space = 0
    # 右边箱子向左移动
    for i in range(mid + 1, n):
        if a[i] == "_":
            space += 1
        if a[i] == "@":
            ans += space
    print(ans)

5. 大石头的搬运工

代码如下:
思想:一堆石头为基准,左边箱子向右移动,右边箱子向左移动,最后遍历看移动到那个箱子的左右值最小

# 推箱子
n = int(input())
a, b = [], []
li = []
#遍历循环输入的n组w和p
for i in range(n):
    li.append(list(map(int,input().split())))
# 先排序,之后才能够合并了再一起移动
li.sort(key=lambda x:x[1])
for x,y in li:
  a.append(x)
  b.append(y)
left = [0]*n
left[0]=a[0]
for i in range(1,n):
  left[i] = a[i] + left[i-1]
# 从右往左计算前缀和
right=[0]*n
right[-1]=a[-1]
for i in range(n-2,-1,-1):
  right[i]=a[i]+right[i+1]
custL =[0]*n
for i in range(1,n):
  custL[i] = custL[i-1]+left[i-1]*(b[i]-b[i-1])
custR = [0]*n
for i in range(n-2,-1,-1):
  custR[i] = custR[i+1]+right[i+1]*(b[i+1]-b[i])
ans = float('inf')
for i in range(n):
  ans = min(ans,custL[i]+custR[i])
print(ans)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

peanut666888

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值