两天速通蓝桥杯

两天速通蓝桥杯

其实是标题党,断断续续准备了不到一个月吧,但是真的是菜比,没有提示做不出来的那种,今天是4.11号,4.13号就要打比赛了,准备把一些临考场能用到的重点整理一下,如果我考完我还有心情写这个笔记,我再复盘一下

很建议提前看一下这两个帖子:

蓝桥杯Python A组国一经验分享(希望这篇文章可以给你一点点帮助)_蓝桥杯国赛一二三等奖比例-CSDN博客

十二天艰难速成蓝桥杯Orz(算法+习题合集)_备战蓝桥杯要多长时间-CSDN博客

把重难点和一些技巧总结的蛮清楚的。

tips

1.建议考前适应一下IDLE

2.递归深度

在这里插入图片描述

背下来sys.setre,然后用Tab键补齐就行

3.excel技巧

【蓝桥杯小技巧】暴力+ Excel的使用(持续更新)_蓝桥杯巧用excel-CSDN博客

读入

之前一直用C,到了python这有点不知道怎么读入

(1)字典

dic = dict()

for _ in range(n):
    ts,id = map(int,input().split())
    #这样会得到{id:ts}
    dic[id] = dic.get(id,[]) + [ts]

#遍历字典
for j in dic.items():
    #这样得到ids为key,ts为冒号后面的内容
    ids,ts_ = (i for i in j)

(2)优先队列

from queue import PriorityQueue

q = PriorityQueue()
#入队
q.put(x)
#出队
x = q.get()
#判断队列是否为空
len(q.queue) != 0

(3)邻接表:

邻接表里存了下一个节点和权重的

G = [[] for i in range(n+1)]

for _ in range(m):
    u,v,w = map(int,input().split())
    G[u].append([v,w])

(4)[1,2,…n]

p = list(range(n+1))

(5)开平方

int(sqry(a))

考试流程解惑

因为我没去线下测试环境,所以对准考证上的比赛流程有点疑惑,下面这篇给的挺详细的

蓝桥杯攻略!省一经验+考试全流程+技巧分享_蓝桥杯可以插u盘吗-CSDN博客
考完之后来现身说法,其实没啥,我们学校的流程就是去考点,找到自己的座位号坐下,然后考试开始前可以操作电脑,查看一下环境(这个因学校而异),我的考点的电脑就有vscode,我全程就用vscode操作的,有的考点只有IDLE,所以熟悉一下IDLE也没毛病。然后基本九点提前一点点,那个考试界面可以下载试题了,你就下载那个压缩包,然后监考老师会告诉你压缩包的密码,你打开就是试题。

下面是一些重要的考点笔记:

搜索

重点!!!DFS BFS,可以暴力求解

DFS

流程:

1.将所有顶点标记为未访问

2.选择一个起始顶点v,访问v

3.选择v的任意一个未被访问的顶点w,访问w,v<-w

4.重复上述步骤3,直到某一节点的所有邻接点都已经被访问,v<-返回到此节点的上一层结点

5.重复步骤3和4,直到返回到根结点,且根节点所有的邻接点都已经被访问

DFS的节点有先序号和后序号两个属性,访问每个结点的时候赋一个先序号,访问该结点结束,要回溯到上一个结点的时候,赋一个后序号

拆分数字

在这里插入图片描述

import os
import sys

def dfs(depth,num):
    if depth == n + 1:
        if sum(num) == x:
            print(*num[1:],sep = ' ')
        return
    for i in range(1,x):
        #要求这个数比前一个数大
        if i > num[depth-1]:
            num[depth] = i
            dfs(depth+1,num)
            

n,x = map(int,input().split())
num = [0] * (n+1)
dfs(1,num)
分糖果

在这里插入图片描述

import os
import sys
ans = 0
#x,y分别为两种糖果剩余的数量
def dfs(depth,x,y):
    global ans
    if depth == 7:
        if x == 0 and y == 0:
            ans += 1
        return
    #第一种糖果可能的数量
    for i in range(0,6):
        if i <= x:
            #第二种糖果可能的数量
            for j in range(0,6):
                if j <= y and 2 <= i + j <= 5:
                    dfs(depth+1,x-i,y-j)
dfs(0,9,16)
print(ans)
回溯

在这里插入图片描述

小朋友崇拜圈

在这里插入图片描述

import os
import sys

n = int(input())
kid =list(map(int,input().split()))
vis = [0] * (n+1)
#用于记录最长路径
len = 0
def dfs(depth,x):
    global len
    vis[x] = depth
    if vis[kid[x]]:
        if vis[x] - vis[kid[x]] + 1 > len:
            len = vis[x] - vis[kid[x]] + 1
        return
    else:
        dfs(depth+1,kid[x])

for i in range(1,n+1):
    if vis[i] == 0:
        dfs(1,i)

print(len)         

BFS

广度有限搜索从起始节点v出发,访问邻接于v的所有顶点,接着从v的一个邻接点出发,访问此节点所有未被访问过的邻接点,依次对v结点的其他邻接点进行类似的操作

利用队:先进先出

1、创建一个空队列queue(用来存放节点)和一个空列表visit(用来存放已访问的节点)

2、依次将起始点及邻接点加入queue和visit中

3、poo出队列中最先进入的节点,从图中获取该节点的邻接点

4、如果邻接点不在visit中,则将该邻接点加入queue和visit中

5、输出pop出的节点

6、重复3、4、5,直至队列为空

最少操作数

在这里插入图片描述

import os
import sys
from collections import deque
vis = [-1] * 100001
def bfs(n,k):
    q = deque()
    q.append(n)
    cnt = 0
    vis[n] = 0
    while len(q) != 0:
        i = q.popleft()
        if i == k:
            return vis[k]
        for j in [i+1,i-1,i*2]:
            #这里加一个判断j是否越界
            if 1<= j <= 100000 and vis[j] == -1:
                vis[j] = vis[i] + 1
                q.append(j)
        

n,k = map(int,input().split())
print(bfs(n,k))

记忆化搜索

在这里插入图片描述在这里插入图片描述在这里插入图片描述

原始:

def f(x):
	if x == 0 or x == 1:
		return 1
	return f(x-1) + f(x-2)

记忆化:

dic = {0:1,1:1}
def f(x):
	if x in dic.keys():
        return dic[x]
    dic[x] = f(x-1) + f(x-2)
    return dic[x]

使用stl:

from functools import lru_cache

@lru_cache(maxsize = None)
def f(x):
    if x == 0 or x == 1:
        return 1
    return f(x-1) + f(x-2)

最短路

floyd

复杂度太高,但是胜在简单

在这里插入图片描述

蓝桥公园

在这里插入图片描述

import os
import sys
n,m,q = map(int,input().split())
INF = int(1e18)
dist = [[INF] * (n+1) for _ in range(n+1)]
for _in range(m):
    u,v,w = map(int,input().split())
    dist[u][v] = w
    dist[v][u] = w

def floyd():
    for k in range(1,n+1):
        for i in range(1,n+1):
            for j in range(1,n+1):
                dist[i][j] = min(dist[i][j],dist[i][k] + dist[k][j])

for _ in range(q):
    st,ed = map(int,input().split())
    if dist[st][ed] != INF:
        print(dist[st][ed])
    else:
        print(-1)
    

dijkstra

最常用,记下来!

在这里插入图片描述

模板
import os
import sys
from queue import PriorityQueue

def dijstra(s):
    q = PriorityQueue()
    q.put((s,0))
    while len(q.queue())!= 0:
        u,_ = q.get()
        
        if vis[u] != 0:
            continue
        vis[u] = 1
        
        for v,w in G[u]:
            d[v] = min(d[v],d[u] + w)
            q.put((v,d[v]))
     for i in range(n+1):
            if d[i] == INF:
                  d[i] = -1
      print(*d[1:],sep=' ')
            
        

n,m = map(int,input().split())
G = [[] for i in range(n+1)]
INF = int(1e18)
d = [INF] * (n+1)
vis = [0] * (n+1)
for _ in range(m):
    u,v,w = map(int,input().split())
    G[u].append([v,w])

dijstra(1)

bellman-ford

有负边再用,应该用不到

在这里插入图片描述

模板

在这里插入图片描述在这里插入图片描述

import os
import sys

def bellman_ford():
    for _ in range(n-1):
        for u,v,w in e:
            #如果v是第n个城市,就不需要隔离
            if v != n:
                res = c[v]
            else:
                res = 0
            if dist[v] = dist[u] + w + res:
                dist[v] = dist[u] + w + res

    #第n次遍历
      for u,v,w in e:
            #这里要判断,如果是第n个城市,就不需要隔离
            if v != n:
                  res = C[v]
            else:
                  res = 0
            if dist[v] > dist[u] + w + res:
                  return -1

n,m, = map(int,input().split())
c = [0] + list(map(int,input().split()))
INF = int(1e18)
e = []
dist = [INF] * (n+1)
for _ in range(m):
    u,v,c = map(int,input().split())
    e.append((u,v,c))

if bellman_ford() == -1:
      print(-1)
else:
      print(dist[n])

模版题

P3371 【模板】单源最短路径(弱化版) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

最小生成树

kruskal

适用于稀疏图

在这里插入图片描述

在这里插入图片描述

def findroot(x):
    if x == p[x]: return x
    p[x] = findroot(p[x])
    return p[x]

def kruskal():
    #遍历每一条边
    for w,u,v in e:
        #查看加入这条边是否构成环
        rootu,rootv = findroot(u),findroot(v)
        if rootu != rootv:
            p[rootu] = rootv
            sum += 1
            max_val = max(max_val,w)
    print(sum,max_val)

n,m, = map(int,input().split())
e = []
for _ in range(m):
    u,v,w = map(int,input().split())
    e.append((w,u,v))

#按照边的权重大小排序
e.sort()
#并查集中存储父节点
p = list(range(n+1))

sum,max_val = 0
kruskal()

prim

适用于稠密图

在这里插入图片描述

同上面kruskal是一个题

#这里发现一个规律,一般加边的都用e.appen(u,v,w)来存储,如果是加点的一般用邻接表或邻接矩阵

n,m, = map(int,input().split())
INF = int(1e18)
G = [[INF] * (n+1) for i in range(n+1)]
for _ in range(m):
    u,v,w = map(int,input().split())
    G[u][v] = G[v][u] = min(G[u][v],w)

d = [INF] * (n+1)
max_val = 0

for i in range(n):
    d[u] = 0
    next_u,next_val = INF
    for v in range(1,n+1):
        #如果是已经加入到集合中的点
        if d[v] == 0: continue
        #如果新加入的点u更新了到点v的最小边,则更新
        d[v] = min(d[v],G[u][v])
        #保存最小边,并且点v作为下一条加入集合的点
        if d[v] < next_val:
            next_val = d[v]
            next_u = v
    u = next_u
    max_val = max(max_val,next_val)
print(n-1,max_val)
        

模板题

[P2330 SCOI2005] 繁忙的都市 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

P1195 口袋的天空 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

树状数组

较难

引出:对于数组求和,我们可以用两两组成一个和的形式储存下来,如下图所示:

如果我们需要求前四个数的和,直接就可以得到19

如果要求前3个数的和,就14+1

在这里插入图片描述

但其实图中有些数据是无用的,比如14的和5,因为求三个数的和和求四个数的和都用不到他。上述数组中无用的部分还有很多,所有层的第偶数个数字都是无用的

在这里插入图片描述

然后我们观察剩下的数据,发现剩下的数据正好有n个,我们可以把这些有用的数据都装进一个数组中,这个数组和原始的数据正好一样长

在这里插入图片描述

求和时,我们只需找到对应的区间,将这些区间相加就可以得到答案

lowbit

在这里插入图片描述

观察树状数组第一行数据

在这里插入图片描述观察第二行
在这里插入图片描述总结:
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

在这里插入图片描述

def lowbit(x):
      return x & (-x)

def update(x,A):
      while x <= n:
            tree[x] += A
            x += lowbit(x)

def query(x):
      sum = 0
      while x:
            sum += tree[x]
            x -= lowbit(x)
      return sum

n = int(input())
a = [0] + list(map(int,input().split()))
m = int(input())
q = []
#初始化树状数组
tree = [0] * (n+1)
for i in range(1,n+1):
      update(i,a[i])


for _ in range(m):
      q = list(map(int,input().split()))
      if q[0] == 1:
            update(q[1],q[2])
            # print(a)
      elif q[0] == 2:
            print(query(q[2]) - query(q[1] - 1))

数论

gcd

1.gcd:利用辗转相除法(欧几里得算法)来求最大公约数。

a,b均能整除gcd(a,b)得到a1,b1;
a1,b1均能整除gcd(a1,b1)得到a2,b2;
……
an-2,bn-2均能整除gcd(an-2,bn-2)得到an-1,1;
最后一次取模为an,0,一遍遍递归即可得到最初a,b的最大公约数

#非递归
def gcd(a,b):
	while b != 0:
        a,b = b, a % b
    return a
#非递归
def gcd(a,b):
    if b == 0: return a
    return gcd(b,a%b)

扩展的欧几里得

在这里插入图片描述

def Exgcd(a,b):
	if b == 0:
		return a,1,0
	d,x,y = Exgcd(b,a%b)
	return d,y,x - (a//b) * y

lcm

lcm就是最小公倍数 其实就是a * b / gcd(a,b)

def lcm(a,b):
	res = a / gcd(a,b)
	return res * b

素数筛法

def isPrime(a):
	if a < 2:
		return False
	for i in range(2,int(sqry(a)) + 1):
		if a % i == 0:
			return False
			
	return True

DP

挺重点的,但是有点考思维

在这里插入图片描述

步骤:

1.拆分子问题

2.确定状态,如dp[x]表示上x个台阶的方案数

3.确定状态转移方程

上台阶

在这里插入图片描述

import os
import sys

MOD = 10000000007

def louti():
    for i in range(1,n+1):
        if i == 1:
            if i not in a:
                dp[i] = 1
            continue
        if i in a:
            continue
        else:
            dp[i] = (dp[i-1] + dp[i-2]) % MOD
    


n,m = map(int,input().split())
a = list(map(int,input().split()))
print(a)
dp = [0] * (n+1)          
dp[0] = 1
louti()
print(dp[n])

选数异或

在这里插入图片描述

异或值的结果最大为63,下面注意一下范围:

原问题:给定n个正整数a,子序列异或为x的方案数

子问题:前i个整数,子序列异或为j的方案数

第i个正整数:选择/不选第i个数字,如何得到dp[i] [j]

不选的话就是dp[i-1] [j] -> dp[i] [j]

选择第i个数字,则就是从第i-1个得到的异或结果xxx 异或 a[i]得到 j

根据异或的性质 xxx等于 j 异或 a[i]

import os
import sys
MOD = 998244353
n,x = map(int,input().split())
a = list(map(int,input().split()))
dp = [[0] * 64 for _ in range(n+1)]
#出口
dp[0][0] = 1
for i in range(n+1):
    for j in range(64):
        dp[i][j] = (dp[i-1][j] + dp[i-1][j ^ a[i]]) % MOD

print(dp[n][x])           

最长公共子序列

在这里插入图片描述

import os
import sys

n,m = map(int,input().split())
a = [0]+list(map(int,input().split()))
b = [0]+list(map(int,input().split()))   
dp = [[0] * (m+1) for _ in range(n+1)]
for i in range(1,n+1):
    for j in range(1,m+1):
        if a[i] == b[j]:
            dp[i][j] = dp[i-1][j-1] + 1
        else:
            dp[i][j] = max(dp[i-1][j],dp[i][j-1])
print(dp[n][m])

输出最长子序列:

path = []
i,j = N,M
while i != 0 and j != 0:
      #print(i,j)
      if dp[i][j] == dp[i-1][j]:
            i = i - 1
      elif dp[i][j] == dp[i][j-1]:
            j = j - 1
      else:
            i,j = i - 1, j - 1
            path.append(a[i])
#倒着打印
print(path[::-1])
01背包

在这里插入图片描述

import os
import sys
N,V = map(int,input().split())
dp = [[0] * (V+1) for x in range(N+1)]

for i in range(1,N+1):
      w,v = map(int,input().split())
      for j in range(V+1):
            if j >= w:
                  dp[i][j] = max(dp[i-1][j],dp[i-1][j-w]+v)
            else:
                  dp[i][j] = dp[i-1][j]

#print(dp)
print(dp[N][V])

滚动数组优化:

在这里插入图片描述

魔法背包

在这里插入图片描述

import os
import sys

n,m,K= map(int,input().split())
dp = [[0] * (2) for i in range(m+1)]
for i in range(n):
    w,v = map(int,input().split())
    for j in range(m,w-1,-1):
        for k in range(2):
            if k == 1 and j >= w + K:
                dp[j][k] = max(dp[j][k],dp[j-w][k] + v,dp[j-K-w][0] + 2 * v)
            else:
                dp[j][k] = max(dp[j][k],dp[j-w][k] + v)

print(max(dp[m][0],dp[m][1]))

分组背包

在这里插入图片描述

import os
import sys
N,V = map(int,input().split())
dp = [[0] * (V+1) for i in range(N+1)]


for i in range(1,N+1):
      s = int(input())
      for k in range(s):
            w,v = map(int,input().split())
            for j in range(V+1):
                  if j < w:
                  		#这里注意,由于是一组中选一个,因此上一轮的dp[i][j](本组中的上一个)是有用的,因此要和新的作比较
                        dp[i][j] = max(dp[i-1][j],dp[i][j])
                  else:
                        dp[i][j] = max(dp[i-1][j],dp[i-1][j-w]+v,dp[i][j])

print(dp[N][V])

区间DP

在这里插入图片描述在这里插入图片描述

import os
import sys
INF = int(1e18)
n = int(input())
a = [0] + list(map(int,input().split()))
dp = [[INF]*(n+1) for _ in range(n+1)]
#还要声明一个前缀和数组
s = [0] * (n+1)
for i in range(1,n+1):
    #用于合并1到i堆石子的花费
    s[i] = s[i-1] + a[i]
    #因此合并i到j堆的花费为s[j] - s[i-1]
    dp[i][i] = 0

#遍历所有区间长度
for len in range(2,n+1):
    #遍历所有左端点
    i = 1
    while i + len - 1 <= n:
        #确定右端点
        j = i + len - 1
        #遍历子区间
        for k in range(i,j):
            dp[i][j] = min(dp[i][j],dp[i][k] + dp[k+1][j] + s[j] - s[i-1])
        i += 1

print(dp[1][n])

字符串

KMP

在这里插入图片描述在这里插入图片描述

首先我们找到了一个失配点,i不动,我们改变j:我们找到S的一个后缀(灰色括号)与T的一个前缀(红色括号)尽可能的匹配(最长),然后我们的j就可以放到红色括号末尾那里,从已经匹配的前缀后开始匹配

我们要求出一个Next数组(较复杂)

在这里插入图片描述

只要不匹配,j = next[j],因为next[j]已经储存了最长公共前后缀的长度

问题变成如何求next数组,next数组使用动态规划来求解:
在这里插入图片描述

我们先看左边的情况

j = next[i] T[0]到T[i-1]的最长后缀上面的浅绿色框,和T的前缀下面的浅绿色框匹配,如果下一个数T[i]与T[j]是匹配的,那么我们就很自然的更新Next[i+1] = j+1,两个数都向后匹配一个数

我们再看右边的情况

因为T[j] != T[i],因为j是前缀,肯定比i小,所以j = Next[j],j变到深绿色部分,看看T[j]与T[i]是否匹配,如果不匹配,还得往前回溯前缀,以此类推

斤斤计较的小z

在这里插入图片描述

import os
import sys
#求解next数组
#Next[i]存储T[0] - T[i-1]的最长公共前后缀
Next = [0] * 1000010
def get_next(t):
    for i in range(1,len(t)):
        j = Next[i]
        #往前找匹配的j
        while j > 0 and t[i] != t[j]:
            j = Next[j]
        if t[i] == t[j]:
            Next[i+1] = j + 1
        else:
            Next[i+1] = 0
#返回s中t出现的次数
def KMP(s,t):
    ans = 0
    j = 0
    for i in range(len(s)):
        while j > 0 and s[i] != t[j]:
            j = Next[j]
        if s[i] == t[j]:
            j += 1
        if j == len(t):
            ans += 1
            j = Next[js
    return ans

字典树

在这里插入图片描述

我们尽可能利用存在的边往下走,如果不存在,则构建新边

也可以对字典树中的每个结点打标记:遍历一次可以给结点的标记数加1,以此统计字符串的前缀数,如以a为前缀的字符串只有ab和aba,因此最开始遍历这两个字符串的时候,aa经过2号结点打一个标记,aba经过2号结点的时候又打了一个标记,以此维护了前缀a的个数

字典树实现的就是增删改查的功能

class TreeNode():
    def __init__(self):
        #表示当前节点往下走的后续节点
        self.node = {}
        #is_leaf是否为终止标志
        self.is_leaf = False
    
    def insert(self,s):
        curr = self
        #遍历字符串s
        for c in s:
            #判断下一步是否能走,不能则新增一个点
            if c in curr.node.keys():
                curr = curr.node[c]
            else:
                curr.node[c] = TreeNode()
                curr = curr.node[c]  
        #字符串走完了,打上结束标志
        curr.is_leaf = True   
    
    def prex(self,s):
        curr = self
        for c in s:
            if c not in curr.node.keys():
                return False
            curr = curr.node[c]
        return True
Tree = TreeNode()

并查集

在这里插入图片描述

模板

#并查集主要有两个操作:找根节点和合并
#设x的范围从1~n

#普通版本
def findroot(x):
    while x != p[x]:
        x = p[x]
    return x
#路径压缩版本
def findroot(x):
    if x == p[x]: return x
    p[x] = findroot(p[x])
    return p[x]

#合并
def Merge(x,y):
    rootx,rooty = findroot(x),findroot(y)
    p[rootx] = rooty

#父节点最开始都初始化为自身
p = list(range(n+1))

复盘

蒽…其实总的来说感觉本次Python不是很难,感觉稍微厉害一点的做个六道以上不是问题。

感觉前填空有点难

第一题:给的是一个很大个数的2*2正方形,和一个较小数的1 * 1正方形,问最大能拼成多长边长的正方形,其实这个我有一些思路,需要找一下规律,但是我当时就是先做后面的题了,最后没时间研究这个。

第二题:应该是判断特殊数字,给的那个例子非常大,需要找规律解决,暴力的话,你在四个小时一定跑不出来,我就没跑出来(悲)

后面六道都是提交代码题了

我记得中间四道都不是很难:但是其实背背模板还是有用的,中间有一道字符串匹配题,我忘了KMP咋写了,我就硬for循环匹配的,还有最小生成树Kruskal和并查集我都用上了

倒数第二道题我感觉就是搜索,但是他那个题我最开始没读明白判断条件要求是啥,改了好多次那个判断条件,我当时觉得能做出来这个,最后一个小时一直在死磕这个,结果那个电脑他比实际的时间慢3分钟,他结束了,我以为还有两分钟交代码,我就没交上(啊啊啊啊)

然后最后一道题也没做,我感觉我是有思路的,然后因为看错时间的缘故,前面两个填空题的答案也没交,蒽…反正第一次蓝桥杯之行就这样结束了,不想再回忆了,就随缘去吧,菜比的自救之路很漫长

但我体感Python赛道的难度还可以,大佬应该没有C++那边多,如果有uu们想比蓝桥,我还是很推荐python赛道的。

  • 24
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值