蓝桥杯倒计时 | 倒计时17天
Posted 让机器理解语言か
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了蓝桥杯倒计时 | 倒计时17天相关的知识,希望对你有一定的参考价值。
作者🕵️♂️:让机器理解语言か
描述🎨:蓝桥杯冲刺阶段,一定要沉住气,一步一个脚印,胜利就在前方!
寄语💓:🐾没有白走的路,每一步都算数!🐾
题目一:22年省赛A题——小兰做实验 (难度:★★)
问题描述
小蓝很喜欢科研, 他最近做了一个实验得到了一批实验数据, 一共是两百 万个正整数。
如果按照预期, 所有的实验数据 x 都应该满足 。但是做实验都会有一些误差, 会导致出现一些预期外的数据, 这种误差数据 y 的 范围是 1 。由于小蓝做实验很可靠, 所以他所有的实验数据中 99.99% 以上都是符合预期的。小蓝的所有实验数据都在 primes.txt 中, 现 在他想统计这两百万个正整
数中有多少个是质数, 你能告诉他吗?
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一 个整数, 在提交答案时只填写这个整数, 填写多余的内容将无法得分。
【思路】
三个考点:
- 读文件。文件有200万个整数,只能读文件,不能复制到代码中。
- 素数判断。判断x是否为素数,可以把它除以[2, sqrt(x)]内的素数,如果能整除,就不是素数。
- 素数筛,预处理出素数。文件里99.99%的整数都< ,所以只要得到内的素数即可。约1300个素数。其他0.01%的大于的数,数量很少,可以单独判断。
计算量:1300×200万。
Python代码实际运行时间:约1分钟。
【代码】
注意:在IDLE运行时,代码和文件必须在同一个文件夹内。
from math import *
def E_sieve(n): # 得到10^4以内的素数
for i in range (2,int (sqrt (n)+1)):
if not vis[i]:
for j in range(i*i, n+1,i):
vis[j] =1
k = 0
for i in range(2,n+1):#存素数
if not vis[i]:
prime[k] = i
k += 1
return k
def is_prime(x): # 判断素数:若n是素数,返回true
if x == 1: return False #1不是素数
for i in range(k):
if x % prime[i] == 0: return False #不是素数
return True #是素数
cnt = 0
N = int(1e4)
prime =[0]*N
vis =[0]*N
n = int(1e4)
k = E_sieve(n-1)
print(k) #质数个数
with open ('primes.txt','r') as f: # 读取文件
for a in f:
b=int(a.rstrip()) #去掉末尾的\\n,转为整数
if b<=int(1e8):
if is_prime(b)==True:cnt +=1
#else:#单独判断大于1e8的数字
print(cnt)
题目二:22年省赛B题——火柴棒数字 (难度:★★★)
问题描述
小蓝最近迷上了用火柴棒拼数字字符, 方法如下图所示:
他只能拼 0 至 9 这十种数字字符, 其中每个数字字符需要的火柴棒的数目 依次是: 6,2,5,5,4,5,6,3,7,6 。他不喜欢重复拼同一个数字字符, 所以对于每个 数字字符他最多拼十个。小蓝会把拼出来的数字字符组合在一起形成一个整数, 例如对于整数 345 , 需要的火柴棒的数目为 5+4+5=14 根。小蓝有 300 根 火柴棒, 他想知道自己能拼出的最大整数是多少? 可以不使用完这 300 根火柴 棒, 可以有多余的火柴棒剩下。
答案提交
这是一道结果填空的题, 你只需要算出结果后提交即可。本题的结果为一 个整数, 在提交答案时只填写这个整数, 填写多余的内容将无法得分。
【思路】
将数字看成价值为1且各有10个的物品,需要的火柴棒看成重量,这是多重背包问题。因为是填空题,没有用滚动数组优化和二进制优化。
- 为什么每个数字价值都为1?
答:因为位数越大,这个整数就越大,最大的整数肯定是位数最多的。(例如:9999<10000),每个数字都相当于给你增加了一位数, 所以每个数字价值都为1。
- 位数相同(价值相同)时,怎么选择?
数字是从小到大装进dp[ ][ ],为了拼出最大的整数,所以输出最大整数时应该按数字i从大到小考虑。当位数相同的时候就选择装了更多数字i的dp,因为装了更多数字i的dp肯定比装了较少的数字i的dp,最后拼出的整数更大。
-
定义状态dpli][j]:表示把前i个数字装进容量j的背包,能装进背包的最大价值。
【代码】
N = 300 # 背包容量
W =[0,6,2,5,5,4,5,6,3,7,6] # 每种火柴的重量
dp = [[0]*301 for i in range(11)]
for i in range(1,11): # 遍历所有数字:数字0为1,数字1为2,以此类推。
for k in range(0,11): # 每个数字最多有10个,每个价值为1
for j in range(k * W[i],301): # 枚举背包容量
dp[i][j] = max(dp[i-1][j], dp[i - 1][j - k * W[i]] + k) # 不装或装第i个数的两种情况取最大值
j=300
for i in range(10,0,-1): # 遍历所有数字,数字9为10,数字8为9,以此类推
k= 0
# 在最大价值下(dp[i][j])最多能装k个数字i
for g in range(0,11): # 遍历每种数字的数量
if j-g*W[i]>=0: # 背包放得下
if (dp[i][j] == dp[i - 1][j - g * W[i]] + g): # 位数(最大价值)相同时,能否装下g个数字i
k = g # 记录最多能装k个
j = j - k * W[i] # 背包剩余容量
while k > 0: # 装了这个数字k个,就输出k个这个数字
k -= 1
print(i - 1, end='')
# 答案:9999999999777777777755555555554444444444333333333322222222221111
题目三:22年省赛C题——近似GCD (难度:★★★)
问题描述
小蓝有一个长度为 n 的数组 A=(a1,a2,⋯,an), 数组的子数组被定义为从原数组中选出连续的一个或多个元素组成的数组。数组的最大公约数指的是数组中所有元素的最大公约数。如果最多更改数组中的一个元素之后, 数组的最大公约数为 g, 那么称 g 为这个数组的近似 GCD。一个数组的近似 GCD 可能 有多种取值。
具体的, 判断 g 是否为一个子数组的近似 GCD 如下:
如果这个子数组的最大公约数就是 g, 那么说明 g 是其近似 GCD。
在修改这个子数组中的一个元素之后 (可以改成想要的任何值), 子数组的最大公约数为 g, 那么说明 g 是这个子数组的近似 GCD。
小蓝想知道, 数组 A 有多少个长度大于等于 2 的子数组满足近似 GCD 的 值为 g 。
输入格式
输入的第一行包含两个整数 n,g, 用一个空格分隔, 分别表示数组 A 的长 度和 g 的值。
第二行包含 n 个整数 a1,a2,⋯,an, 相邻两个整数之间用一个空格分隔。
输出格式
输出一行包含一个整数表示数组 A 有多少个长度大于等于 2 的子数组的近 似 GCD 的值为 g 。
样例输入
5 3 1 3 6 4 10
样例输出
5
样例说明
满足条件的子数组有 5 个:
[1,3]: 将 1 修改为 3 后, 这个子数组的最大公约数为 3 , 满足条件。
[1,3,6]: 将 1 修改为 3 后, 这个子数组的最大公约数为 3 , 满足条件。
[3,6]:这个子数组的最大公约数就是 3 , 满足条件。
[3,6,4] : 将 4 修改为 3 后, 这个子数组的最大公约数为 3 , 满足条件。
[6,4] : 将 4 修改为 3 后, 这个子数组的最大公约数为 3 , 满足条件。
评测用例规模与约定
对于 20% 的评测用例, 2≤n≤10^2 ;
对于 40% 的评测用例, 2≤n≤10^3;
对于所有评测用例, 2≤n≤1≤g,ai≤10^9 。
n最大是 ,所以最多只能用O(nlogn)的算法。
【思路】
题解
一个子数组a:
- 若a中每一项都是g的倍数,只需要将a中某个元素改为g,满足要求。
- 若a中存在一个不是g的倍数,把它改为g,满足要求。
- 若a中存在至少2个不是g的倍数,无法满足要求。
问题变成:求原数组有多少个子数组满足这个数组里最多只有一个元素不是g的倍数编码
用双指针遍历原数组:双指针复杂度:O(n)
【代码】
技巧1:用last标记当前的不满足g的倍数的点,下次a[i]再次不满足g的倍数时,j直接跳到last往后一点(即last+1),确保只有一个不是g的倍数
技巧2: 每次i移动一步就记录一下双指针的距离(即满足要求的子数组个数),然后将它们累加起来,就是所有满足要求的子数组个数
n,g=map(int,input().split())
a=[0]+list(map(int,input().split()))
ans=0
last=0
j=1
for i in range(1, n+1): # 前指针往前走
if a[i]%g != 0: # 不满足g的倍数
j = last+1 # 后指针往前走
last = i # last标记不满足点,下次a[i]再次不满足g的倍数,j直接跳到last往后一点
if i-j+1 >= 2:ans += i-j # 长度大于2就开始累加
print(ans)
蓝桥杯倒计时 | 倒计时4天
作者🕵️♂️:让机器理解语言か
描述🎨:蓝桥杯冲刺阶段,一定要沉住气,一步一个脚印,胜利就在前方!
寄语💓:🐾没有白走的路,每一步都算数!🐾
题目一:数的计算
题目描述
我们要求找出具有下列性质数的个数(包含输入的自然数 n):
先输入一个自然数 n (n≤1000),然后对此自然数按照如下方法进行处理:
不作任何处理;
在它的左边加上一个自然数,但该自然数不能超过该数最高位的一半;
加上数后,继续按此规则进行处理,直到不能再加自然数为止。
输入描述
输入一个正整数 n。
输出描述
输出一个整数,表示具有该性质数的个数。。
输入输出样例
输入
6
输出
6
样例分析
例: n=6,合法的数字有: 6(不做任何处理) 、16、26、36、126、136
解题思路
按照题目意思,我们可以直接枚举左边加的数。
递归
定义递归函数 f(n) 表示输入数为 n 时满足题目条件的数的个数。
我们可以从最简单的情况开始考虑。当 n=1 时,1的一半向下取整为0,没办法构造,直接返回。
如果 n>1,那么我们需要枚举左边加的数。因为最左边的数不能为 0,所以左边加上的数的取值范围是 [1,⌊n/2⌋]。
对于每一个加数 i,得到的新数是 n+i,我们需要递归调用 f(n+i),计算得到新数下满足条件的数的个数。
在递归调用结束后,我们需要将所有加数得到的满足条件的数的个数相加,得到最终的结果。
最后,输出 f(n) 即可。
递推
AC_code(递归)
def f(n):
global res
if n == 1: # 1的一半向下取整为0,没办法构造,直接返回
return
for i in range(1, n//2+1): # 枚举左边加的数,其中n//2:向下取整
res += 1
f(i) # 递归
n = int(input())
res = 1
f(n)
print(res)
AC_code(递推)
n = int( input( ))
f = [0 for i in range(n+1)]
for i in range( 1, n + 1): # 从f[1]开始算
for j in range(1, i//2 + 1): # 累加
f[i]= f[i] + f[j] # 递推式
f[i] += 1 # 自己本身
print(f[n])
题目二:数的划分
题目描述
将整数 n 分成 k 份,且每份不能为空,任意两份不能相同(不考虑顺序)。
例如:n=7,k=3,下面三种分法被认为是相同的。
1,1,5;1,5,1;5,1,1;
问有多少种不同的分法。
输入描述
输入一行,2 个整数 n,k (6≤n≤200,2≤k≤6)。
输出描述
输出一个整数,即不同的分法。
输入输出样例
输入
7 3
输出
4
解题思路
定义递归函数 f(n,m) 为将整数 n 拆分成 m 个数字的方案数。
对于每个情况,我们可以将它分成两种情况,且这两种情况是不重不漏的。
- 不选 1 的情况:
如果不选择 1,我们将 n 拆分成 m 块,可以等价于将每一块都减去 1,然后再将剩下的数拆分成 m 块,即 f(n−m,m)。
- 选 1 的情况:
这种情况下,其中一块肯定有一个 1,然后对 n−1 拆分成 m−1 块,即 f(n−1,m−1)。
此时,f(n,m) 的值就是这两种情况之和,即f(n,m)=f(n−m,m)+f(n−1,m−1)
需要注意的是,当 n<m 时,无法分成 m 个数,因此 f(n,m) = 0
另外,当 m=1 时,只能将 n 拆分成一个数,因此 f(n,1) = 1;当n=m时,只能是每块1个,所以f(i, i)=1
最终的答案为 f(n,k) 。
AC_code
由于 python递归的速度极慢,因此我们可以使用动态规划的思想,将递归改为递推,代码如下:
n, k = map(int, input().split())
# 初始化一个二维数组,用于存储 f(n, m)
dp = [[0 for j in range(210)] for i in range(210)]
for i in range(1, n+1):
dp[i][1] = 1
dp[i][i] = 1
for i in range(3, n+1):
for j in range(2, k+1):
if i > j: # 只有n大于m才能分出多种情况
dp[i][j] = dp[i-j][j] + dp[i-1][j-1]
print(dp[n][k])
题目三:耐摔指数
题目描述
X 星球的居民脾气不太好,但好在他们生气的时候唯一的异常举动是:摔手机。
各大厂商也就纷纷推出各种耐摔型手机。X 星球的质监局规定了手机必须经过耐摔测试,并且评定出一个耐摔指数来,之后才允许上市流通。
X 星球有很多高耸入云的高塔,刚好可以用来做耐摔测试。塔的每一层高度都是一样的,与地球上稍有不同的是,他们的第一层不是地面,而是相当于我们的 2 楼。
如果手机从第 7 层扔下去没摔坏,但第 8 层摔坏了,则手机耐摔指数 = 7。
特别地,如果手机从第 1 层扔下去就坏了,则耐摔指数 = 0。
如果到了塔的最高层第 n 层扔没摔坏,则耐摔指数 = n。
为了减少测试次数,从每个厂家抽样 3 部手机参加测试。
如果已知了测试塔的高度,并且采用最佳策略,在最坏的运气下最多需要测试多少次才能确定手机的耐摔指数呢?
输入描述
一个整数 n(3<n<10000),表示测试塔的高度。
输出描述
输出一个整数,表示最多测试多少次。
输入输出样例
输入
3
输出
2
样例解释
手机 a 从 2 楼扔下去,坏了,就把 b 手机从 1 楼扔;否则 a 手机继续 3 层扔下。
解题思路
设 bi 表示需要 i 个球时最少要有多少层楼,ci 表示最少需要多少个球才能测出 i 层楼,初始化 b1=c1=1,由于球的数量不会超过 100100,故开数组 b 和 c 大小均为 105105。
当需要测的楼层数为 n 时,从 11 开始枚举,如果 ci 大于等于 n,那么 i 即为需要的最少球的数量,输出后退出。
当 ci 小于 n 时,需要再增加一个球,求出测 i+1 层楼所需的最小楼层数 bi+1 和测 i+1 层楼所需的最少球数 ci+1,由于测第 i+1 层楼时,要么球碎了,要么没碎,因此:
- 如果球碎了,需要在楼下测 bi 层楼,此时用剩下的 i−1 个球去测楼下的这 bi 层楼,共需要 i 个球,即 ci+1=ci+i。
- 如果球没碎,需要在上面测剩下的 i 个球,即去测 i−1 层楼,此时共需测 i+1 层楼,需要在楼上再增加一层楼,因此用剩下的 i−1 个球去测这 i 层楼,共需要 bi 层楼,即 ci+1=ci+bi+1。
因此得到递推公式:bi+1=bi+i+1;ci+1=ci+bi+1
AC_code
b = [0] * 105
c = [0] * 105
n = int(input())
i = 0
while c[i] < n:
i += 1
b[i] = i + b[i - 1]
c[i] = c[i - 1] + b[i - 1] + 1
print(i)
以上是关于蓝桥杯倒计时 | 倒计时17天的主要内容,如果未能解决你的问题,请参考以下文章