【算法竞赛学习笔记】基础算法篇:前缀和

前言

本文为个人学习的算法学习笔记,学习笔记,学习笔记不是经验分享与教学,不是经验分享与教学,不是经验分享与教学,若有错误各位大佬轻喷(T^T)。主要使用编程语言为Python3,各类资料题目源于网络,主要自学途径为蓝桥云课,侵权即删。

算法思想

前缀和:对于一个长度为n的列表a,其前缀和sum[i] = a[0] + a[1] + ...+ a[i],得到一个新的列表sum称为前缀和。

性质:

1.迭代性:sum[i] = sum[i-1] + a[i]

def get_persum(a):
    sum0 = [0] * len(a)
    sum0[0] = a[0]
    for i in range(len(a)):
        sum0[i] = sum0[i-1] + a[i]

2.区间求法:a[r] + ... + a[l] = sum[l] - sum[r-1]

def get_sum(sum0, l, r):
    ans = 0
    if(l == 0):
        ans = sum0[r]
    else:
        ans = sum[r] - sum[l - 1]

    return ans

题目实战1

以下题目均来自蓝桥云课题库,链接如下

题库 - 蓝桥云课

1.区间次方和

问题描述

给定一个长度为 nn 的整数数组 aa 以及 mm 个查询。

每个查询包含三个整数 l,r,kl,r,k 表示询问 l∼rl∼r 之间所有元素的 kk 次方和。

请对每个查询输出一个答案,答案对 10^9+7取模。

输入格式

第一行输入两个整数 n,mn,m 其含义如上所述。

第二行输入 nn 个整数 a[1],a[2],...,a[n]a[1],a[2],...,a[n]。

接下来 mm 行,每行输入三个整数 l,r,kl,r,k 表示一个查询。

输出格式

输出 mm 行,每行一个整数,表示查询的答案对 10^9+7 取模的结果。

错误实录

import os
import sys

# 请在此输入您的代码
#求前缀和列表函数
def get_persum(a, n):
  sum0 = []
  sum0[0] = a[0]
  for i in range(1,n):
    sum0[i] = sum0[i-1] + a[i]
  return sum0

#求前缀和区间函数
def get_sum(sum0, l, r):
  if l == 0:
    return sum0[r]
  else:
    return sum0[r] - sum0[l-1]

#列表读入函数
def input_list():
  return list(map(int, input().split()))

#输入
n, m = input_list()
a_origin = input_list()

while m != 0:
  l, r, k = input_list()
  a = []
  i = 0
  for i in range(len(a_origin)-1):
      a[i] = a_origin[i]**k
  sum0 = []
  sum0 = get_persum(a, n)
  ans = get_sum(sum0, l, r)
  print(ans)
  print()
  m -= 1
  

错误点

a = []  # 创建空列表
a[0] = 1  # 报错!空列表没有索引0

正确的列表初始化方法

a = []  # 初始化空列表
for i in range(len(a_origin)):
    a.append(a_origin[i] ** k)  # 正确:使用append添加元素

解答

import os
import sys

# 求前缀和列表函数
def get_persum(a, n):
    sum0 = [0] * n
    sum0[0] = a[0]
    for i in range(1, n):
        sum0[i] = sum0[i-1] + a[i]
    return sum0

# 求前缀和区间函数(修改:调整索引)
def get_sum(sum0, l, r):
    # 输入的l,r从1开始,转换为从0开始的索引
    l -= 1
    r -= 1
    if l == 0:
        return sum0[r]
    else:
        return sum0[r] - sum0[l-1]

# 列表读入函数
def input_list():
    return list(map(int, input().split()))

# 输入
n, m = input_list()
a_origin = input_list()

while m != 0:
    l, r, k = input_list()
    a = []
    for i in range(len(a_origin)):
        a.append(a_origin[i] ** k) 
    sum0 = get_persum(a, n)
    ans = get_sum(sum0, l, r)
    ans = ans % (10**9 + 7)
    print(ans)
    m -= 1

2.小郑的平衡串

问题描述

平衡串指的是一个字符串,其中包含两种不同字符,并且这两种字符的数量相等。

例如,abababababab 和 aababbaababb 都是平衡串,因为每种字符各有三个,而 abaababaab 和 aaaabaaaab 都不是平衡串,因为它们的字符数量不相等。

平衡串在密码学和计算机科学中具有重要应用,比如可以用于构造哈希函数或者解决一些数学问题。

小郑拿到一个只包含 LL、QQ 的字符串,他的任务就是找到最长平衡串,且满足平衡串的要求,即保证子串中 LL、QQ 的数量相等。

输入格式

输入一行字符串,保证字符串中只包含字符 LL、QQ。

输出格式

输出一个整数,为输入字符串中最长平衡串的长度。

解答

import os
import sys

# 请在此输入您的代码

def get_persum(a):
  sum0 = [0] * len(a)
  sum0[0] = a[0]
  for i in range(1, len(a)):
    sum0[i] = sum0[i-1] + a[i]

  return sum0

def get_sum(sum0, l, r):
  if l == 0:
    ans = sum0[r]
  else:
    ans = sum0[r] - sum0[l-1]
  return ans

a = ""

a = input()

code = []

maxstr = 0

for i in range(len(a)):
  if(a[i] == 'L'):
    code.append(1)
  else:
    code.append(-1)

sum0 = get_persum(code)

for l in range(len(code)):
  for r in range(l, len(code)):
    if(get_sum(sum0, l, r) == 0):
      maxstr = max(maxstr,r - l + 1)

print(maxstr)

体悟:问题提及区间,大多数情况就会用到前缀和

算法拓展——二维前缀和

tips:对于二维列表,编程过程中建议下标都从1开始

区间和求法

#打印矩阵函数
def output(a, n):
    for i in range(1, n+1):
        print(''.join(map(str, a[i][1:])))

#输入二维矩阵行列数
n, m = list(map(int, input().split()))
#读入二维矩阵
a = [[0] * (m+1) for _ in range(n+1)]
sum = [[0] * (m+1) for _ in range(n+1)]
for i in range(1,n+1):
    a[i] = [0] + list(map(int, input().split()))
output(a, n)
#构造二位前缀和
for i in range(1, n+1):
    for j in range(1, m+1):
        sum[i][j] = sum[i][j-1] + sum[i-1][j] - sum[i-1][j-1] + a[i][j]
        

#二维前缀和区间和函数
def get_sum(sum, x1, y1, x2, y2):
    return sum[x2][y2] - sum[x1-1][y2] - sum[x2][y1-1] + sum[x1-1][y1-1]

题目实战2

1.统计子矩阵

问题描述

给定一个 N×MN×M 的矩阵 AA, 请你统计有多少个子矩阵 (最小 1×11×1, 最大 N×M)N×M) 满足子矩阵中所有数的和不超过给定的整数 KK ?

输入格式

第一行包含三个整数 N,MN,M 和 KK.

之后 NN 行每行包含 MM 个整数, 代表矩阵 AA.

输出格式

一个整数代表答案。

import os
import sys

# 请在此输入您的代码

#初始化准备
n, m, k = map(int, input().split())

ans = 0

#读入二维矩阵
a = [[0] * (m+1) for _ in range(n+1)]
sum0 = [[0] * (m+1) for _ in range(n+1)]
for i in range(1,n+1):
    a[i] = [0] + list(map(int, input().split()))

#二维前缀和区间和函数
def get_sum(sum0, x1, y1, x2, y2):
    return (sum0[x2][y2] - sum0[x1-1][y2] - sum0[x2][y1-1] + sum0[x1-1][y1-1])

for i in range(1, n+1):
  for j in range(1, m+1):
    sum0[i][j] = sum0[i][j-1] + sum0[i-1][j] -sum0[i-1][j-1] + a[i][j]

for x1 in range(1,n+1):
  for y1 in range(1,m+1):
    for x2 in range(x1, n+1):
      for y2 in range(y1, m+1):
        if(get_sum(sum0, x1, y1, x2, y2) <= k):
          ans += 1

print(ans)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值