蓝桥杯python省赛冲刺篇2——常用算法的详细解析及对应蓝桥杯真题:打表模拟法、递推递归法、枚举法、贪心算法、差分与前缀和

注意:加了题目链接

一、打表模拟法

介绍

模拟法是比赛中最常用的方法,使用各种算法大都离不开模拟,而对于一些只是需要结果的题目来说打表法是一个非常好的解决方案,而且对于数论等其他需要找规律的题目来说,打表法是一个非常有用的方法。这种方法一般多数出现在填空题中。

下面给出几道关于打表模拟法的蓝桥杯真题。

1. 算式问题

链接:算式问题

题目描述

本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。

看这个算式:
☆☆☆ + ☆☆☆ = ☆☆☆
如果每个五角星代表 1 1 1 ~ 9 9 9 的不同的数字。
这个算式有多少种可能的正确填写方法?

173 + 286 = 459
295 + 173 = 468
173 + 295 = 468
183 + 492 = 675

以上都是正确的填写法!
注意: 111 + 222 = 333 111 + 222 = 333 111+222=333 是错误的填写法! 因为每个数字必须是不同的! 也就是说: 1 1 1 ~ 9 9 9中的所有数字,每个必须出现且仅出现一次!

注意:不包括数字 “ 0 ” “0” 0

注意: 满足加法交换率的式子算两种不同的答案。 所以答案肯定是个偶数!

解析与代码演示

对于这道题,一眼我们就知道可以用暴力枚举的方法来解决,因为这是一道填空题,所以我们可以不管时间的长短,写完直接让程序跑起来就行。

根据题目要求,最小的三位数是123,最大的三位数是987,所以我们可以从123枚举到987。

count = 0
for a in range(123, 988):
    for b in range(123, 988-a):
        c = a + b
        if c < 1000:
            s = str(a) + str(b) + str(c)
            if '0' not in s:
                if len(set(s)) == 9:
                    count += 1


print(count)

答案:336

2. 求值

链接:求值

题目描述

本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。

学习了约数后,小明对于约数很好奇,他发现,给定一个正整数 t t t,总是可以找到含有 t t t 个约数的整数。小明对于含有 t t t 个约数的最小数非常感兴趣,并把它定义为 S t S_t St

例如 S 1 = 1 , S 2 = 2 , S 3 = 4 , S 4 = 6 , ⋅ ⋅ ⋅ S_1 = 1, S_2 = 2, S_3 = 4, S_4 = 6,· · · S1=1,S2=2,S3=4,S4=6
现在小明想知道,当 t = 100 t = 100 t=100 时, S t S_t St是多少?即 S 100 S_{100} S100是多少?

解析与代码演示

这道题是要求找出含有100个约数的数字,我们第一步可以先写一个一个数字含有的约数总和,第二步就可以直接暴力枚举了。

def cnt(a):
    ans=0

    for j in range(1,a+1):
        if a%j ==0:
            ans+=1
    return   ans
for i in range(1,400000):
    if cnt(i)==100:
        print(cnt(i),' ',i)
        break

答案:45360

3. 既约分数

链接:既约分数

题目描述

本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。

如果一个分数的分子和分母的最大公约数是 1 1 1,这个分数称为既约分数。

例如 3 4 , 1 8 , 7 1 \frac{3}{4} ,\frac{1}{8} ,\frac{7}{1} 43,81,17 , 都是既约分数。
请问,有多少个既约分数,分子和分母都是 1 1 1 2020 2020 2020 之间的整数(包括 1 1 1 2020 2020 2020)?

解析与代码演示

def gcd2(a, b):
    while b > 0:
        r = a % b
        a = b
        b = r
    return a
count = 0
for i in range(1, 2021):
    for j in range(1, 2021):
        if gcd2(i, j) == 1:
            count += 1
print(count)

答案:2481215

4. 天干地支

链接:天干地支

题目描述

古代中国使用天干地支来记录当前的年份。

天干一共有十个,分别为:甲(jiǎ)、乙(yǐ)、丙(bǐng)、丁(dīng)、戊(wù)、己(jǐ)、庚(gēng)、辛(xīn)、壬(rén)、癸(guǐ)。

地支一共有十二个,分别为:子(zǐ)、丑(chǒu)、寅(yín)、卯(mǎo)、辰(chén)、巳(sì)、午(wǔ)、未(wèi)、申(shēn)、酉(yǒu)、戌(xū)、 亥(hài)。

将天干和地支连起来,就组成了一个天干地支的年份,例如:甲子。

2020 2020 2020 年是庚子年。

每过一年,天干和地支都会移动到下一个。例如 2021 2021 2021 年是辛丑年。

每过 60 60 60 年,天干会循环 6 6 6 轮,地支会循环 5 5 5 轮,所以天干地支纪年每 60 60 60 年轮回一次。例如 1900 1900 1900 年, 1960 1960 1960 年, 2020 2020 2020 年都是庚子年。

给定一个公元纪年的年份,请输出这一年的天干地支年份。

输入描述

输入一行包含一个正整数,表示公元年份。

其中有 ,输入的公元年份为不超过 9999 9999 9999 的正整数。

输出描述

输入一行包含一个正整数,表示公元年份。

输入输出样例

示例

输入

2020

输出

2020

解析与代码演示

我们可以先判断 0000 年的天干 和地支。天干是10年一个轮回,又已知2020年是庚子年,所以2020 % 10 = 0,0000 年的天干就是庚,2020 % 12 = 4,往前推四年,地支是申。由此我们可以通过模拟法来推到。

  • N-1%10=0 时 天干为庚
  • N-1%10=1 时 天干为辛
  • N-1%12=0 时 地支为申
  • N-1%12=1 时 地支为酉
tg = ["geng", "xin", "ren", "gui", "jia", "yi", "bing", "ding", "wu", "ji"]
dz = ["shen", "you", "xu", "hai", "zi", "chou",
      "yin", "mou", "chen", "si", "wu", "wei"]
    
year = int(input())
print(tg[year % 10], dz[year % 12], sep='')

二、递推递归法

介绍

递推算法的思想

  • 首要问题是先找到各个相邻数据项之间的递推关系;
  • 递推关系避开了求通项公式的麻烦,且有些题目的通项公式很难求,或者不能进行求解;
  • 将复杂问题分解为若干步骤的简单运算;
  • 一般来说递推算法就是一种特殊的迭代算法。
    递归算法设计的一般步骤
  1. 根据题目设计递归函数中的运算部分;
  2. 根据题目找到递归公式,题目可能会隐含给出,也可能需要自己进行推导;
  3. 找到递归出口,即递归的终止条件。

下面给出几道关于递推递归法的蓝桥杯真题。

1. 斐波那契数列

链接:斐波那契数列

题目描述

斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,其指的是这样一个数列: 0 、 1 、 1 、 2 、 3 、 5 、 8 、 13 、 21 、 34 、 ⋯ 0、1、1、2、3、5、8、13、21、34、\cdots 0112358132134

在数学上,斐波那契数列以如下被以递推的方法定义: F ( 0 ) = 0 , F ( 1 ) = 1 , F ( n ) = F ( n − 1 ) + F ( n − 2 ) ( n ≥ 2 , n ∈ N ∗ ) F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n \in N^*) F(0)=0F(1)=1,F(n)=F(n1)+F(n2)n2nN

现给定一个正整数 n n n,请求出该数列中第 n n n 项的值是多少。

输入描述

输入仅 1 1 1 行,包含一个整数 N N N 0 ≤ N ≤ 25 0\leq N \leq 25 0N25

输出描述

输出一个整数,表示答案。

输入输出样例

示例

输入

3

输出

2

解析与代码演示

对于题目而言,已经给出了递推公式:
F(n) = F(n-1) + F(n-2),也就是F(n) + F(n+1) = F(n+2)
所以这道题既可以用递归也可以用递推。
对于递归:
如:
F(6) = F(5) + F(4) = F(4) + F(3) + F(3) + F(2) = …
到最后肯定都是F(0)和F(1),所以需要设置终止条件

def f(x):
    if x == 0:  # 终止条件
        return 0
    if x == 1:  # 终止条件
        return 1
    return f(x-1)+f(x-2)
n = int(input())
print(f(n))

对于递推:
我们需要设置好递推的起点,也就是F(0)和F(1),之后让递推公式一直循环下去。

n = int(input())

f_0 = 0 # F(n)
f_1 = 1 # F(n+1)
f_n = 0 # F(n+2)暂时为空
if n == 0:
    f_n = 0
elif n == 1:
    f_n = 1
else:
    for i in range(n-1):
        f_n = f_0 + f_1 # F(n+2) = F(n) + F(n+1)
        # 更新下轮的数值
        f_0 = f_1 # F(n) = F(n+1)
        f_1 = f_n # F(n+1) = F(n+2)
        
print(f_n)

2. 斐波那契数列多次查询版

链接:斐波那契数列多次查询版

题目描述

斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,其指的是这样一个数列: 0 、 1 、 1 、 2 、 3 、 5 、 8 、 13 、 21 、 34 、 ⋯ 0、1、1、2、3、5、8、13、21、34、\cdots 0112358132134

在数学上,斐波那契数列以如下被以递推的方法定义: F ( 0 ) = 0 , F ( 1 ) = 1 , F ( n ) = F ( n − 1 ) + F ( n − 2 ) ( n ≥ 2 , n ∈ N ∗ ) F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n \in N^*) F(0)=0F(1)=1,F(n)=F(n1)+F(n2)n2nN

现给定一个正整数 M M M,表示 M M M 个查询。每个查询给定一个正整数 N N N,请求出该数列中第 N N N 项的值是多少。

输入描述

1 1 1 行为一个整数 T T T,表示测试数据数量。

接下来的 T T T 行每行包含一个正整数 N N N

1 ≤ T ≤ 5 × 1 0 2 , 1 ≤ N ≤ 30 。 1 \leq T \leq 5\times 10^2 ,1\leq N \leq 30。 1T5×1021N30

输出描述

输出共 T T T 行,每行包含一个整数,表示答案。

输入输出样例

示例

输入

2
3
4

输出

2
3

解析与代码演示

这道题无疑是再上一题的基础加上一个查询操作,由于N在1~30范围内,我们可以把斐波那契数列的前三十项存在列表中,方便查询。

注意:在蓝桥杯测评时,只会用你的输出来比较,所以我们只关心答案即可!不用害怕输入输出写在一起。

n = int(input())

def func(n):
    f_0 = 0 # F(n)
    f_1 = 1 # F(n+1)
    f_n = 0 # F(n+2)暂时为空
    if n == 0:
        f_n = 0
    elif n == 1:
        f_n = 1
    else:
        for i in range(n-1):
            f_n = f_0 + f_1 # F(n+2) = F(n) + F(n+1)
            # 更新下轮的数值
            f_0 = f_1 # F(n) = F(n+1)
            f_1 = f_n # F(n+1) = F(n+2)
            
    return f_n
a = []
for i in range(0, 30):
    a.append(func(i))

for j in range(n):
    x = int(input())
    print(a[x])

3. 数字三角形

链接:数字三角形

题目描述

在这里插入图片描述
上图给出了一个数字三角形。从三角形的顶部到底部有很多条不同的路径。对于每条路径,把路径上面的数加起来可以得到一个和,你的任务就是找到最大的和(路径上的每一步只可沿左斜线向下或右斜线向下走)。

输入描述

输入的第一行包含一个整数 N   ( 1 ≤ N ≤ 100 ) N\ (1 \leq N \leq 100) N (1N100),表示三角形的行数。

下面的 N N N 行给出数字三角形。数字三角形上的数都是 0 0 0 99 99 99 之间的整数。

输出描述

输出一个整数,表示答案。

输入输出样例

示例

输入

5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5

输出

30

解析与代码演示

这道题的解法可以用动态规划、暴力枚举。从递推的方法看。当我们在第i层,第j列时,在选择下一条路径时肯定时选择最大的那一条,即 a [ i ] [ j ] = m a x { a [ i ] [ j ] + a [ i + 1 ] [ j ] , a [ i ] [ j ] + a [ i + 1 ] [ j + 1 ] } a[i][j]=max\lbrace a[i][j]+a[i + 1][j],a[i][j]+a[i + 1][j+1] \rbrace a[i][j]=max{a[i][j]+a[i+1][j]a[i][j]+a[i+1][j+1]},显而易见,从第一行开始是不可行的,所以我们要采用逆推的方法,从最后一层开始向上推。

a = [[0] * 101] * 101


n = int(input())

# 输入数字三角形的值
for i in range(1, n+1):
    a[i] = input().split()
    a[i] = list(map(int, a[i]))


for i in range(n - 1, 0, -1):
    # 最后一层逆推
    for j in range(0, i):

        # 路径选择
        if a[i + 1][j] >= a[i + 1][j + 1]:
            a[i][j] += a[i + 1][j]

        else:
            a[i][j] += a[i + 1][j + 1]

print(a[1][0])

三、枚举法

介绍

枚举的基本思想
将问题的所有可能成为答案的解一一列举,然后根据问题所给出的条件判断此解是否合适,如果合适就保留,反之则舍弃。需要注意的是枚举最重要的时把所有情况都列举出来,缺一不可。

下面给出几道关于打表模拟法的蓝桥杯真题。

1. 42 点问题

链接:42 点问题

题目描述

众所周知在扑克牌中,有一个老掉牙的游戏叫做 24 24 24 点:选取 4 4 4 张牌进行加减乘除,看是否能得出 24 24 24 这个答案。

现在小蓝同学发明了一个新游戏,他从扑克牌中依次抽出 6 6 6 张牌(注意不是一次抽出),进行计算,看是否能够组成 42 42 42 点 , 满足输出 YES,反之输出 NO

请你设计一个程序对该问题进行解答。

输入描述

输出仅一行包含 6 6 6 个字符。
保证字符 ∈ 3 4 5 6 7 8 9 10 J Q K A 2

输出描述

若给出到字符能够组成 42 42 42 点 , 满足输出 YES,反之输出 NO

输入输出样例

示例

输入

K A Q 6 2 3 

输出

YES

样例说明

  • K A = K 即 131 = 13 KA=K即131=13 KA=K131=13
  • 13/12=1 保留整数
  • 1+6=7
  • 7∗2=14
  • 14∗3=42

解析与代码演示

对于这道题,我们可以进行暴力枚举,因为是6个数依此运算,我们就可以创建5个列表来依此存储它们5次运算的结果,就是前一个数和后一个数的加减乘除的运算结果。

a = list(map(str, input().split()))
for i in range(len(a)):
    if a[i] == 'A':
        a[i] = 1
    elif a[i] == 'J':
        a[i] = 11
    elif a[i] == 'Q':
        a[i] = 12
    elif a[i] == 'K':
        a[i] = 13
    else:
        a[i] = ord(a[i]) - ord('0')
l1 = [[],[],[],[],[]]
l1[0].append(a[0]+a[1])
l1[0].append(a[0]-a[1])
l1[0].append(a[0]*a[1])
l1[0].append(int(a[0]/a[1]))
for i in range(1, 5):
    for j in range(len(l1[i-1])):
        # 运算前一个列表内所有的数字与后一个数字的加减乘除
        l1[i].append(l1[i-1][j] + a[i+1])
        l1[i].append(l1[i-1][j] - a[i+1])
        l1[i].append(l1[i-1][j] * a[i+1])
        l1[i].append(int(l1[i-1][j] / a[i+1]))

flag = 0
for i in range(len(l1[-1])):
    if l1[-1][i] == 42:
        flag = 1
        break
    else:
        continue
if flag == 1:
    print("YES")
else:
    print("NO")
   

2. 公平抽签

链接:公平抽签

题目描述

A A A的学校,蓝桥杯的参赛名额非常有限,只有 m m m 个名额,但是共有 n n n 个人报名。

作为老师非常苦恼,他不知道该让谁去,他在寻求一个绝对公平的方式。

于是他准备让大家抽签决定,即 m m m 个签是去,剩下的是不去。

A A A非常想弄明白最后的抽签结果会有多少种不同到情况,请你设计一个程序帮帮小 A A A

输入描述

输入第一行包含两个字符 n , m n,m n,m,其含义如题所述。

接下来第二行到第 n + 1 n+1 n+1 行每行包含一个字符串 S S S ,表示个人名。

1 ≤ m ≤ n ≤ 15 1≤m≤n≤15 1mn15

输出描述

输出共若干行,每行包含 m m m 个字符串,表示该结果被选中到人名。

输入输出样例

示例

输入

3 2
xiaowang
xiaoA
xiaoli

输出

xiaowang xiaoA
xiaowang xiaoli
xiaoA xiaoli

解析与代码演示

对于这道题,我们可以使用排列组合的方式进行求解,由于python自带求排列组合的库,我们就可以不用自己写啦。


import itertools
n, m = map(int, input().split())
l1 = []
l2 = []
for i in range(n):
    name = input()
    l1.append(name)
for i in range(1,len(l1)+1):
    iter = itertools.combinations(l1,i)
    l2.append(list(iter))
l3 = l2[m-1]
for name in l3:
    print(' '.join(list(name)))
# print(l2)

3. 座次问题

链接:座次问题

题目描述

A A A 的学校,老师好不容易解决了蓝桥杯的报名问题,现在老师又犯愁了。

现在有 N N N 位同学参加比赛,但是老师想给他们排座位,但是排列方式太多了。

老师非常想弄明白最后的排座次的结果是什么样子的,到底有多少种结果。

请设计一个程序帮助老师。

最后输出各种情况的人名即可,一行一种情况,每种情况的名字按照报名即输入顺序排序。

输入描述

输入第一行包含一个整数 N N N
接下来 N N N 行每行包含一个字符串 S i S_i Si,表示人名。
1 ≤ N ≤ 10 , ∑ i = 1 N ∣ S i ∣ ≤ 1 0 2 。 1≤N≤10,\sum\limits_{i=1}^{N} |S_i| \leq 10^2 。 1N10i=1NSi102

输出描述

输出共若干行,每行输出各种情况的人名。一行一种情况,每种情况的名字按照报名即输入顺序排序。

输入输出样例

示例

输入

3
xiaowang
xiaoA
xiaoli

输出

xiaowang xiaoA xiaoli
xiaowang xiaoli xiaoA
xiaoA xiaowang xiaoli
xiaoA xiaoli xiaowang
xiaoli xiaowang xiaoA
xiaoli xiaoA xiaowang

解析与代码演示

这题也是非常的简单,一眼看出就是全排列,我们可以直接使用python自带的全排列的库。

from itertools import permutations
n = int(input())
l1 = []
for i in range(n):
    name = input()
    l1.append(name)
l2 = list(permutations(l1))
for names in l2:
    print(' '.join(list(names)))

四、贪心算法

介绍

贪心算法,又称贪婪算法。是一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而使得问题得到全局最优解。

贪心算法主要适用于最优化问题,如:MST 问题。有时候贪心算法并不能得到最优答案,但是能得到精确答案的近似答案。有时可以辅助其他算法得到不是那么精确的结果。

适用范围
所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。
贪心算法的解题思路

  1. 证明原问题的最优解之一可以由贪心选择得到。
  2. 将最优化问题转化为这样一个问题,即先做出选择,再解决剩下的一个子问题。
  3. 对每一子问题一一求解,得到子问题的局部最优解;
  4. 把子问题的解局部最优解合成原来解问题的一个解

下面给出几道关于打表模拟法的蓝桥杯真题。

1. 找零问题

链接:找零问题

题目描述

蓝桥商店的老板需要找零 n n n 元钱。
钱币的面额有: 100 100 100 元、 50 50 50 元、 20 20 20 元、 5 5 5 元、 1 1 1 元,问如何找零使得所需钱币的数量最少?

注意: n n n 可能为 0 0 0,也能为几百元(别问,问就是来着里微信提现来了)

输入描述

在第一行给出测试例个数 N N N,代表需要找零的钱数。
1 ≤ N ≤ 1 0 5 1≤N≤10^5 1N105

输出描述

输出共有 5 5 5 行,每一行输出数据输出找零的金额与数量,详情看样例。

示例

输入

365

输出

100:3
50:1
20:0
5:3
1:0

解析与代码演示

对于这道题,我们需要找零使得所需钱币的数量最少,那我肯定就要从金额大的面额开始找,如365,我们就需要先找3个100,剩下65又可以找一个50,剩下15,20的面额不够,就可以用3个5元的面额。

a = [100, 50, 20, 5, 1]
n = int(input())
for i in range(len(a)):
    print(str(a[i])+':'+str(n//a[i]))
    n = n%a[i]

2. 贪心的自助餐

链接:贪心的自助餐

题目描述

B B B同学想去吃自助餐,但是他是那种比较节俭的的人,既不想浪费食物,又想尽可能吃的贵一点,他于是私下里做了调查。

小蓝餐厅的自助餐有 n n n 种食材,每种食材都有它的价格。

而且也能估计出每一份的重量,所以他列了一个表格:

菜品价格(元)重量(g)
红烧牛肉30300
油闷大虾85
四喜丸子48
三文鱼53
排骨18200
麻辣兔头20120
高汤海参4070
扇贝粉丝832
牛排79240

B B B的饭量为 C ( g ) C(g) C(g),他想知道在不超过饭量的情况下他最多能吃多少钱的菜品。

请你设计一个程序帮助小 B B B计算他的最多吃了多少钱。(假设自助餐厅的菜品供应同样的菜品每个人只能取一份。)

输入描述

第一行输入两个整数 n , C n , C ( 0 ≤ n ≤ 1 0 3 , 0 ≤ C ≤ 1 0 4 ) n,Cn,C(0\leq n\leq 10^3,0\leq C\leq 10^4) n,Cn,C0n1030C104,其中 n n n 为菜品数量, C C C 为小 B B B的肚子容量。

接下来 n n n 行每行输入两个数 v [ i ] , w [ i ] , v [ i ] v[i],w[i],v[i] v[i],w[i]v[i] 是第 i i i 个菜品的价值, w [ i ] w[i] w[i] 表示第 i i i 个菜品的重量 ( 0 ≤ v [ i ] , w [ i ] ≤ 1 0 4 ) (0\leq v[i],w[i] \leq 10^4) 0v[i],w[i]104

输出描述

输出一行数据,表示最大的价值,保留小数点后三位数。

示例

输入

20 1000
1 22
2 43
123 214
12 2
123 432
21 223
22 16
77 49
34 78
34 9
43 677
21 34
23 23
12 56
332 56
21 99
123 545
389 33
12 999
23 88

输出

1204.114

解析与代码演示

对于这道题总是要挑贵的吃,但一方面人会饱,所以我们需要按照每个事物的单价来挑选事物,尽量选贵的。



# 菜的数量、能吃的重量
n, c = map(int, input().split())
l1 = []
for i in range(n):
    # 价格 重量
    v, w = map(int, input().split())
    l1.append((v, w))
# 按单价价格最高来排序
l1 = sorted(l1, key=lambda x:x[0]/x[1], reverse=True)
i = 0
money = 0
while i < len(l1):


    if l1[i][1] >= c:
        money = money + (l1[i][0] / l1[i][1]) * c
        break
    else:
        money += l1[i][0]
        c -= l1[i][1]
    i += 1
print('%.3f'%(money))

五、差分与前缀和

5.1 差分法

介绍

差分法的应用主要是用于处理区间问题。当某一个数组要在很多不确定的区间,加上相同的一个数。我们如果每个都进行加法操作的话,那么复杂度是 O(nm) ,非常消耗时间。如果采用差分法,我们可以将O(nm)降低到O(n)。

解题步骤

  1. b[0] = a[0]
  2. 从第 2 项到 n 项,利用 b[i]=a[i]-a[i-1] 差分式(从第一项开始);
  3. 对于区间端点操作加减;
  4. 差分还原(前缀和)。

例子:
对a = [1,2,3,4,5,7,2]中从第二个到第五个元素都加3。

a = [1,2,3,4,5,7,2]
b = [0 for i in range(len(a))]
# 第一步:b[0] = a[0]
b[0] = a[0]
# 第二步:差分
for i in range(1, len(a)):
    b[i] = a[i] - a[i-1]
# 第三步:对区间t1, t2+1进行加减
b[1] += 3
b[5] -= 3
# 第四步: 差分还原(前缀和)
for i in range(1, len(a)):
    b[i] += b[i-1]
print(b)

下面给出关于打表模拟法的蓝桥杯真题。

1. 大学里的树木要打药

链接:大学里的树木要打药

题目描述

教室外有 N N N 棵树(树的编号从 0 ∼ N − 1 0\sim N-1 0N1),根据不同的位置和树种,学校要对其上不同的药。

因为树的排列成线性,且非常长,我们可以将它们看作一条直线给他们编号。

对于树的药是成区间分布,比如 3 ∼ 5 3 \sim 5 35号的树靠近下水道,所以他们要用驱蚊虫的药, 20 ∼ 26 20 \sim 26 2026号的树,他们排水不好,容易涝所以要给他们用点促进根系的药 ⋯ \cdots 诸如此类。

每种不同的药要花不同的钱。

现在已知共有 M M M 个这样的区间,并且给你每个区间花的钱,问最后这些树木要花多少药费。

输入描述

每组输入的第一行有两个整数 N N N M M M N N N 代表马路的共计多少棵树, M M M 代表区间的数目, N N N M M M 之间用一个空格隔开。

接下来的 M M M 行每行包含三个不同的整数,用一个空格隔开,分别表示一个区域的起始点 L L L 和终止点 R R R 的坐标,以及花费。

1 ≤ L ≤ R ≤ N ≤ 1 0 6 , 1 ≤ M ≤ 1 0 5 1\leq L\leq R \leq N \leq 10^6,1\leq M\leq 10^5 1LRN106,1M105,保证花费总和不超过 int 范围。

输出描述
示例

输入

500 3
150 300 4
100 200 20
470 471 19

输出

2662
解析与代码演示

对于这道题,我们可以使用一个全为0的a数组,长度为n,初始化,所有的价格为0,用差分法给每个区件做加法,最后求出数组之和。

n, m = map(int, input().split())
a = [0 for i in range(n)]
s = [0 for i in range(len(a))]
for i in range(1, len(a)):
    s[i] = a[i] - a[i-1]
s[0] = a[0]
for i in range(m):
    t1, t2, v = map(int, input().split())
    s[t1-1] += v
    s[t2] -= v
for i in range(1, len(a)):
    s[i] += s[i-1]
print(sum(s))

5.2 前缀和

介绍

前缀和是指某序列的前 n 项和,当对于某一数组区间进行多次询问,[L,r] 的和时,如果正常处理,那么我们每次都要 [l,r]。查询 N 次,那么时间复杂度也是 O(nm) 。
解题步骤

  1. 利用b[i] = a[i-1] + b[i-1]求出前缀和
  2. 初始化时b[0] = 0
  3. 利用端点相减求区间和b[n] - b[m-1]

例子:求a = [1,2,3,4,5,7,2]第二个到第五个元素的和

a = [1,2,3,4,5,7,2]
b = [0 for i in range(len(a)+1)]
# 第一步:利用b[i] = a[i-1] + b[i-1]求出前缀和
for i in range(1, len(a)+1):
    b[i] = b[i-1] + a[i-1]
print(b)
# 第三步
print(b[5]-b[2-1])

下面给出关于打表模拟法的蓝桥杯真题。

1. 大学里的树木要维护

链接:大学里的树木要维护

题目描述

教室外有 N N N 棵树(树的编号从 1 ∼ N 1\sim N 1N),根据不同的位置和树种,学校已经对其进行了多年的维护。

因为树的排列成线性,且非常长,我们可以将它们看作一条直线给他们编号。

由于已经维护了多年,每一个树都由学校的园艺人员进行了维护费用的统计。

每棵树的前期维护费用各不相同,但是由于未来需要要打药,所以有些树木的维护费用太高的话,就要重新种植。

由于维护费用也称区间分布,所以常常需要统一个区间里的树木的维护开销。

现给定一个长度为 N N N 的数组 A A A 以及 M M M 个查询, A i A_i Ai
​表示第 i i i 棵树到维护费用。对于每个查询包含一个区间,园艺人员想知道该区间内的树木维护的开销是多少。

请你编写程序帮帮他!

输入描述

每组输入的第一行有两个整数 N N N M M M N N N 代表马路的共计多少棵树, M M M 代表区间的数目, N N N M M M 之间用一个空格隔开。

接下来的一行,包含 N N N 个数 A 1 , A 2 , ⋯   , A N A_1,A_2,\cdots,A_N A1,A2,,AN
​,分别表示每棵树的维护费用,每个数之间用空格隔开。

接下来的 M M M 行每行包含两个不同的整数,用一个空格隔开,表示一个区域的起始点 L L L 和终止点 R R R 的坐标。

输出描述

输出包括 M M M 行,每一行只包含一个整数,表示维护的开销。

输入输出描述
示例

输入

10 3
7 5 6 4 2 5 0 8 5 3
1 5
2 6
3 7

输出

24
22
17
解析与代码演示
n, m = map(int, input().split())
a = list(map(int, input().split()))
b = [0 for i in range(len(a)+1)]
for i in range(1, len(a)+1):
    b[i] = b[i-1] + a[i-1]
for i in range(m):
    t1, t2 = map(int, input().split())
    print(b[t2] - b[t1-1])
  • 7
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值