蓝桥杯第22天(Python)(疯狂刷题第5天)

题型:

1.思维题/杂题:数学公式,分析题意,找规律

2.BFS/DFS:广搜(递归实现),深搜(deque实现)

3.简单数论:模,素数(只需要判断到 int(sqrt(n))+1),gcd,lcm,快速幂(位运算移位操作),大数分解(分解为质数的乘积)

4.简单图论:最短路(一对多(Dijstra,临接表,矩阵实现),多对多(Floyd,矩阵实现)),最小生成树(并查集实现)

5.简单字符串处理:最好转为列表操作

6.DP:线性DP,最长公共子序列,0/1背包问题,最长连续字符串,最大递增子串

7.基本算法:二分,贪心,组合,排列,前缀和,差分

8.基本数据结构:队列,集合,字典,字符串,列表,栈,树

9.常用模块:math,datetime,sys中的设置最大递归深度(sys.setrecursionlimit(3000000)),collections.deque(队列),itertools.combinations(list,n)(组合),itertools.permutations(list,n)(排列)  heapq(小顶堆)

目录

1.裁纸刀(思维)

2.寻找整数

3.《质因数个数》真题练习(大数分解)

 4.《矩形拼接》真题练习(枚举遍历)

5.《消除游戏》(暴力循环)​编辑

6.重新排序(差分数组,贪心)

7.《全排列的价值》真题练习(数学定理,思维)

 8. 《最长不下降子序列》真题练习(DP)

 9.《最优清零方案》真题练习(暴力,线段树)

 10.《数的拆分》真题练习


1.裁纸刀(思维)

蓝桥杯第22天(Python)(疯狂刷题第5天)_第1张图片

 裁的次数是一定的!找规律打印输出即可。

2.寻找整数

蓝桥杯第22天(Python)(疯狂刷题第5天)_第2张图片

import sys  #设置递归深度
import collections  #队列
import itertools  # 排列组合
import heapq  #小顶堆
import math
sys.setrecursionlimit(300000)
import functools   # 自定义比较函数  -1不变,1交换


def lcm(x,y):
   return x//math.gcd(x,y)*y

x=3
step=1

b=[0,0,
   1,2,1,4,5,4,1,2,9,0,5,10,
   11,14,9,0,11,18,9,11,11,15,17,9,
   23,20,25,16,29,27,25,11,17,4,29,22,
   37,23,9,1,11,11,33,29,15,5,41,46
   ]

'''
3 5 7 8 9 11 13 15 17 19 21 23 25 27      2递增
5 8 11 14 17 20 23 26 29 32     3递增筛选    5 11 17 23  
5 9 13 17 21 25 29 33     4递增    5 17 29

'''


for i in range(2,50):  # 类似埃式筛法
   while x %i !=b[i]:   
      x+=step
   step=lcm(step,i)  # 更新步长
print(x)



 暴力遍历或者找规律,根据前面几个找规律

3.《质因数个数》真题练习(大数分解)

蓝桥杯第22天(Python)(疯狂刷题第5天)_第3张图片

标程:
 

n = int(input())
ans = 0
#从2开始进行质因子分解
i = 2
while i * i <= n:
    if n % i == 0:
        ans += 1
        while n % i == 0:
            n //= i
    i += 1
if n != 1:
    ans += 1
print(ans)
from random import randint
from math import gcd

def witness(a, n):
    u = n - 1
    t = 0
    while u % 2 == 0:
        u = u // 2
        t += 1
    x1 = pow(a, u, n)
    for i in range(1, t + 1):
        x2 = x1 * x1 % n
        if x2 == 1 and x1 != 1 and x1 != n - 1:
            return True
        x1 = x2
    if x1 != 1:
        return True
    return False

#miller_rabin素性测试 对数字n进行s次测试
def miller_rabin(n, s = 5):
    if n < 2:
        return False
    if n == 2:
        return True
    if n % 2 == 0:
        return False
    for i in range(s):
        a = randint(1, n - 1)
        if witness(a, n):
            return False
    return True

#返回一个因子,不一定是素因子
def pollard_rho(n):
    i, k = 1, 2
    c = randint(1, n - 1)
    x = randint(0, n - 1)
    y = x
    while True:
        i += 1
        x = (x * x + c) % n
        d = gcd(abs(x - y), n)
        if d != 1 and d != n:
            return d
        if y == x:
            return n
        if i == k:
            y = x
            k = k * 2

factor = []
#找所有的素因子
def findfac(n):
    if miller_rabin(n):
        factor.append(n)
        return
    p = n
    while p >= n:
        p = pollard_rho(p)
    findfac(p)
    findfac(n // p)

n = int(input())
findfac(n)
print(len(set(factor)))

 4.《矩形拼接》真题练习(枚举遍历)

蓝桥杯第22天(Python)(疯狂刷题第5天)_第4张图片

 蓝桥杯第22天(Python)(疯狂刷题第5天)_第5张图片

 依次考虑4条边,6条边,8条边对应的情况,枚举遍历

标程:

T = int(input())
while T != 0:
    T -= 1
    a = list(map(int, input().split()))
    a = [[a[0],a[1]], [a[2],a[3]], [a[4],a[5]]]

    ans = 8
    #枚举第一个矩形下标为i,第二个矩形下标为j,第三个矩形下标为k
    for i in range(3):
        for j in range(3):
            for k in range(3):
                if i == j or i == k or j == k:
                    continue
                #枚举三个矩形的两条边
                for ii in range(2):
                    for jj in range(2):
                        for kk in range(2):
                            if a[i][ii] == a[j][jj]:
                                ans = min(ans, 6)
                                if a[i][ii] == a[k][kk]:
                                    ans = min(ans, 4)
                            if a[i][ii] == a[j][jj] + a[k][kk]:
                                ans = min(ans, 6)
                                if a[j][1 - jj] == a[k][1 - kk]:
                                    ans = min(ans, 4)

    print(ans)

5.《消除游戏》(暴力循环)蓝桥杯第22天(Python)(疯狂刷题第5天)_第6张图片

暴力循环 ,扫一轮,看哪些是边缘字符,记录下标,完成扫描后删除,完成后继续循环遍历,退出条件:当前字符为空或者循环一次后长度不变。

标程:

s = list(input())
last_length = 0

while True:
    length = len(s)
    #如果长度等于0,终止
    if length == 0:
        print("EMPTY")
        break
    #如果长度未发生变化,终止
    if length == last_length:
        print("".join(s))
        break
    vis = [0] * length
    #根据题意找出边缘字符
    for i in range(length):
        if (i - 1) >= 0 and (i + 1) < length and s[i] == s[i - 1] and s[i] != s[i + 1]:
            vis[i] = vis[i + 1] = 1
        if (i - 1) >= 0 and (i + 1) < length and s[i] != s[i - 1] and s[i] == s[i + 1]:
            vis[i] = vis[i - 1] = 1
    #将边缘字符去除
    tmp_s = []
    for i in range(length):
        if vis[i] == 0:
            tmp_s.append(s[i])
    s = tmp_s
    last_length = length

6.重新排序(差分数组,贪心)

蓝桥杯第22天(Python)(疯狂刷题第5天)_第7张图片

蓝桥杯第22天(Python)(疯狂刷题第5天)_第8张图片

 初步想法:将重新查询的区间进行记录,看是否有交集,将交集区间替换为最大值

差分数组蓝桥杯第22天(Python)(疯狂刷题第5天)_第9张图片

 正解:读取区间,标记区间访问次数(通过差分数组实现),然后根据贪心思想,将大的值放到访问次数最多的位置。

标程:

import sys  #设置递归深度
import collections  #队列
import itertools  # 排列组合
import heapq  #小顶堆
import math
sys.setrecursionlimit(300000)
import functools   # 自定义比较函数  -1不变,1交换


# 总体思路:查询最多的那一个放最大值,不需要序号,只需要记录最大次数
n = int(input())
a = list(map(int,input().split()))
a=[0]+a
b=[0]*(n+10)
s=[0]*(n+1)

m=int(input())
for i in range(m):
   # 差分数组实现区间加法更新
   l,r = map(int,input().split())
   b[l]+=1
   b[r+1]-=1

#对差分数组前缀和,得到每个数字的查询次数
for i in range(1,n+1):
   s[i]=s[i-1]+b[i]

# sum1为原始和,sum2为贪心后的最大值
sum1,sum2=0,0
for i in range(1,n+1):
   sum1+=a[i]*s[i]

# 贪心思想,大对大,小对小
a.sort()
s.sort()

# 计算重新排序后的
for i in range(1,n+1):
   sum2+=a[i]*s[i]
print(sum2-sum1)



7.《全排列的价值》真题练习(数学定理,思维)

蓝桥杯第22天(Python)(疯狂刷题第5天)_第10张图片

蓝桥杯第22天(Python)(疯狂刷题第5天)_第11张图片

初步想法:找规律?没得规律就把全排列列出来,循环暴力,能拿多少分那多少分。

 找规律,蒙对20%数据

import sys  #设置递归深度
import collections  #队列
import itertools  # 排列组合
import heapq  #小顶堆
import math
sys.setrecursionlimit(300000)
import functools   # 自定义比较函数  -1不变,1交换


#4 0+1*3+2*3+3*3+4*3+5*3+6
#3 0+1+1+2+2+3
#2 0+1

ans=0
n = int(input())
for i in range(1,n*(n-1)//2):
   ans+=(n-1)*i%998244353
print((ans+n*(n-1)//2)%998244353)

 正解:顺序数和逆序数总和为 n*(n-1)//2   ,顺序数和逆序数想等,n个数公有n!个全排列,即顺序和逆序之和为n!*n*(n-1)//2,所以价值之和为上式除以2。

标程:

mod = 998244353
n = int(input())
ans = n * (n - 1) // 2 % mod
for i in range(3, n + 1):
    ans = ans * i % mod
print(ans)

 8. 《最长不下降子序列》真题练习(DP)

蓝桥杯第22天(Python)(疯狂刷题第5天)_第12张图片

初步想法:通过DP算法,即最长递增子序列模板来做,记录最长子序列的下标,通过DP数组来找遍历,从后往前,看是否有元素小于最长子序列同时区间大于K的,有的话直接+k?

应该有问题,这种思路有问题!!不能过全部数据,考虑不全。

正解:思路类似,通过DP最长递增模板,但是需要用线段树模板维护,这题不要全分,跳了。

 9.《最优清零方案》真题练习(暴力,线段树)

蓝桥杯第22天(Python)(疯狂刷题第5天)_第13张图片

 初步想法:先选择操作2,在操作1,暴力循环就可以了

 正解:思路相同,但是我的想法不能过全部数据,需要用线段树来处理,线段树没学。

maxn = 1000000 + 10
tree_mi = [0] * (maxn * 4)
tree_add = [0] * (maxn * 4)

n, k = list(map(int, input().split()))
a = list(map(int, input().split()))
a = [0, *a]

#线段树模板
#利用左右儿子信息更新节点o
def push_up(o):
    tree_mi[o] = min(tree_mi[o << 1], tree_mi[o << 1 | 1])

#利用节点o的lazy标记add更新左右儿子
def push_down(o):
    if tree_add[o] != 0:
        tree_add[o << 1] += tree_add[o]
        tree_mi[o << 1] += tree_add[o]
        tree_add[o << 1 | 1] += tree_add[o]
        tree_mi[o << 1 | 1] += tree_add[o]
        tree_add[o] = 0

#建树
def build(o, l, r):
    tree_add[o] = 0
    if l == r:
        tree_mi[o] = a[l]
        return
    mid = (l + r) >> 1
    build(o << 1, l, mid)
    build(o << 1 | 1, mid + 1, r)
    push_up(o)

#查询区间[L,R]的最小值
def query(o, l, r, L, R):
    if L <= l and r <= R:
        return tree_mi[o]
    push_down(o);
    mid = (l + r) >> 1
    ans = 1000000000;
    if L <= mid:
        ans = min(ans, query(o << 1, l, mid, L, R))
    if R > mid:
        ans = min(ans, query(o << 1 | 1, mid + 1, r, L, R))
    return ans

#区间更新[L,R]统一加上val
def update(o, l, r, L, R, val):
    if L <= l and r <= R:
        tree_mi[o] += val
        tree_add[o] += val
        return
    push_down(o);
    mid = (l + r) >> 1
    if L <= mid:
        update(o << 1, l, mid, L, R, val)
    if R > mid:
        update(o << 1 | 1, mid + 1, r, L, R, val)
    push_up(o);


build(1, 1, n)
ans = 0
for i in range(1, n - k + 2):
    #查询区间[i, i+k-1]的最小值
    mi = query(1, 1, n, i, i + k - 1)
    if mi == 0:                     #无法进行区间消除
        #res表示当前的a[i]
        res = query(1, 1, n, i, i)
        #把当前的a[i]置为0
        update(1, 1, n, i, i, -res)
        ans += res
    else:
        ans += mi
        #区间消除
        update(1, 1, n, i, i + k - 1, -mi)
        #res表示当前的a[i]
        res = query(1, 1, n, i, i)
        #把当前的a[i]置为0
        update(1, 1, n, i, i, -res)
        ans += res
for i in range(n - k + 2, n + 1):
    ans += query(1, 1, n, i, i)
print(ans)

 10.《数的拆分》真题练习

蓝桥杯第22天(Python)(疯狂刷题第5天)_第14张图片

 蓝桥杯第22天(Python)(疯狂刷题第5天)_第15张图片

 初步想法:大数分解应该能做,看是否能分解为两个质数或者一个质数,能就输出yes,不能就输出no

import os
import sys
import math

# 请在此输入您的代码
t= int(input())
def check(n):
  count=[]  # 记录有多少个素数
  cur=0  # 指向列表,方便记录幂次
  for i in range(2,int(math.sqrt(n))+1):
    if n%i==0:
      count.append(0)
      if len(count)>2:
        print("no")
        return
      while n%i==0:
        n=n//i
        count[cur]+=1
      cur+=1  #记录下一个质数幂次
  if n>1:
    count.append(1)
    if len(count)>2:
      print("no")
      return
  #print(count)
  if len(count)==1 and count[0]>=2:
    print('yes')
    return
  if  len(count)==2 and count[0]>=2 and count[1]>=2:
    print('yes')
    return
  print('no')

for i in range(t):
  a=int(input())
  check(a)

正解:题目意思好像弄错了,本题求的是只要分解的质数的幂次大于2就行。(标准做法要用到埃氏筛得到4000内的所有素数,对每一个数遍历,首先判断平方和立方,然后再枚举4000以内的素数因子,如果有幂次等于1的,直接跳出循环打印no)

import os
import sys
import math

# 请在此输入您的代码
t= int(input())
def check(n):
  count=0  # 记录幂次
  for i in range(2,int(math.sqrt(n))+1):
    if n%i==0:
      while n%i==0:
        n=n//i
        count+=1
      if(count<2):
        print('no')
        return
      count=0   # 复位,记数下一个质数
  if n>1:  # 剩下一个质数,幂次肯定为1,不满足
      print("no")
      return
  print('yes')

for i in range(t):
  a=int(input())
  check(a)

标程:

import os
import sys
import math

not_prime = [0]*4010
prime = []
# 预处理400以内的素数
for i in range(2,4001):
  if not_prime[i]==0:
    prime.append(i)
    for j in range(2*i,4001,i):  #埃式筛
        not_prime[j]=1

# 判断平方数
def square_number(x):
  y=int(x**0.5)
  return y*y==x or (y+1)*(y+1)==x   #防止精度出问题


# 判断立方数
def cubic_number(x):
  y=int(x**(1/3))
  return y**3==x or (y+1)**3 ==x
t = int(input())
for i in range(t):
  a=int(input())
  # 判断平方和立方
  if square_number(a) or cubic_number(a):
    print('yes')
    continue
  # 枚举4000以内因子
  falg=True
  for i in prime:
    if a%i==0:
      mi=0
      while a%i==0:
        a=a//i
        mi+=1
      if mi==1:
        falg=False
        break
  if falg:
    print('yes')
  else:
    print('no')
      
      

你可能感兴趣的:(蓝桥杯,蓝桥杯,算法,python)