提莫莫的Python算法总结,快来看看哟~
文章目录
输入输出
# 读一行连续的的字符存数组,如abcd
a = list(input())
# 读一行带有空格的字符存数组,如a b c d
a = input().split()
# 读一行连续的的数字存数组,如12345
a = list(map(int, input()))
# 读一行带有空格的数字存数组,如1 2 3 4 5
a = list(map(int, input().split()))
数学规律
已知矩形n*m:
长方形总个数(包括正方形):
(n*m*(n+1)*(m+1))/4
单个正方形n*m的个数
(m-min{n,m}+1)*(n-min{n,m}+1)
所有正方形的个数需要累加
数据结构
在python就有Queue这个库,但Queue库是一个用来进行线程间通信,用于多线程的高性能栈。虽然它有FIFO(先入先出)队列Queue,LIFO(后入先出)栈LifoQueue,和优先级队列PriorityQueue,但速度较慢,且不能不出栈地访问头部元素,想要访问头部元素,只能用get方法出栈首部获取方法返回值的来进行访问,非常不方便
Queue的底层实现用的是collections.deque,这是一个双端队列,大概同C++中的deque,首尾都可以添加删除元素,可以用来模拟queue和stack。
栈
操作(以que为例) | Python | C++ |
---|---|---|
取队首 | s[-1] | s.top() |
入队 | s.append() | s.push() |
出队 | s.pop() | s.pop() |
import collections
# stack
s = collections.deque()
s.append(3)
s.append(5)
s.append(7)
print("count:{},size:{},front:{}".format(s.count(3), len(s),s[-1]))
s.pop()
print("after_pop_front:{}".format(s[-1]))
# 运行结果
# count:1,size:3,front:7
# after_pop_front:5
队列
queue大致用法:
操作 | Python | C++ |
---|---|---|
取队首 | que[0] | que.front() |
入队 | que.append() | que.push() |
出队 | que.popleft() | que.pop() |
import collections
# queue
q = collections.deque()
q.append(10)
q.append(4)
q.append(11)
print("top:{}".format(q[0]))
q.popleft()
print("after_pop_top:{}".format(q[0]))
# 运行结果:
# top:10
# after_pop_top:4
优先队列
方法一:
python中没有标准的priorityqueue,假如你非常习惯C++的可以写个这样的类
import heapq
# 小顶堆
class PriorityQueue:
def __init__(self):
self.__queue = []
def push(self, priority):
heapq.heappush(self.__queue, priority)
def pop(self):
return heapq.heappop(self.__queue)
def front(self):
return self.__queue[0]
q = PriorityQueue()
q.push(2)
q.push(1)
q.push(3)
q.push(1)
print(q.front())
print(q.pop())
print(q.pop())
print(q.pop())
print(q.pop())
# 大顶堆需要自己加负号
方法二:
import queue
# 只有小顶堆
q = queue.PriorityQueue()
q.put(1)
q.put(5)
q.put(2)
q.put(9)
print(q.get())
print(q.get())
print(q.get())
print(q.get())
# 1 2 5 9
q.put([1,'aaa'])
q.put([5,'ccc'])
q.put([2,'bbb'])
q.put([9,'ddd'])
while not q.empty():
printf(q.get())
'''
[1, 'aaa']
[2, 'bbb']
[5, 'ccc']
[9, 'ddd']
'''
put 函数的第一个 参数表示的是当前这个数据的优先级,第二个参数是值,优先级的值越小,则表明优先级越高,越先被取出。
库
下面的示例,应该能看懂
itertools
排列函数
In [1]: items = ['a', 'b', 'c']
In [2]: from itertools import permutations
In [3]: for p in permutations(items):
...: print(p)
...:
('a', 'b', 'c')
('a', 'c', 'b')
('b', 'a', 'c')
('b', 'c', 'a')
('c', 'a', 'b')
('c', 'b', 'a')
In [4]: for p in permutations(items, 2):
...: print(p)
...:
('a', 'b')
('a', 'c')
('b', 'a')
('b', 'c')
('c', 'a')
('c', 'b')
组合函数
In [5]: from itertools import combinations
In [6]: for c in combinations(items, 3):
...: print(c)
...:
('a', 'b', 'c')
In [7]: for c in combinations(items, 2):
...: print(c)
...:
('a', 'b')
('a', 'c')
('b', 'c')
In [8]: for c in combinations(items, 1):
...: print(c)
...:
('a',)
('b',)
('c',)
itertools.product(*iterables[, repeat])
product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
踩坑
定义二维数组
# 方法1 直接定义
matrix = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
# 方法2 间接定义
matrix = [[0 for j in range(3)] for i in range(3)]
错误示例
# 错误示例
matrix = [[0, 0, 0]]*3
matrix = [[0, 0, 0]]*3操作中,只是创建3个指向array的引用,所以一旦array改变,matrix中3个list也会随之改变。
全局变量
按照C++样式写的算法,例如深搜里面的ans变量,要注意去声明为全局变量
小技巧
由于俺系蓝桥杯选手,所以技巧是面向蓝桥杯的。
第一时间先找本机是否装有VS Code或Anaconda或Pycharm,不然用IDLE这个记事本写代码就比较蛋疼,有就用,没有就忍着吧。
忘记了函数名怎么办?C艹和Java组会提供文档查阅,Python好像不提供。不过问题不大,我们可以寻求帮助help()
。
# 查看math库
print(dir(math))
# ['__doc__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'comb', 'copysign', 'cos', 'cosh', 'degrees', 'dist', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'isqrt', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 'perm', 'pi', 'pow', 'prod', 'radians', 'remainder', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'tau', 'trunc']
# 查看math库帮助及所有函数
print(help(math))
# 几百行,显示所有函数的的英文说明
# 查看math库具体函数
print(help(math.sqrt))
模板
常用的简单模板,那些高端的模板表示不会ORZ,有些是将C++版的直接改写
欧拉筛
# 埃氏筛法
# isPrime = [True for _ in range(101)]
#
# for i in range(2, len(isPrime)):
# if isPrime[i]:
# j = 2
# while j * i <= 100:
# isPrime[i * j] = False
# j += 1
#
#
# result = []
#
# # print(isPrime)
# for num in range(2, len(isPrime)):
# if isPrime[num]:
# result.append(num)
#
# print(result)
# print(len(result))
# 欧拉筛法
N = int(input("请输入一个大于2的整数:"))
result = list()
isPrime = [True for _ in range(N)]
for i in range(2, N):
if isPrime[i]:
result.append(i)
for e in result:
if e * i >= N:
break
isPrime[e * i] = False
if i % e == 0:
break
print(result)
二分答案
直接上个例题来说明吧
砍伐机工作流程如下:提莫师兄需要先设置一个高度参数H(米),砍伐机会升起一个巨大的锯片到高度H,并砍伐掉所有比H高的部分(对于不高于H的部分保持不变)。提莫师兄就得到了蘑菇被砍伐下的部分。例如,如果一排蘑菇的高度分别为6,6和7,提莫师兄把锯片升到4米的高度,则可以从第一颗蘑菇得到2米,从第二颗蘑菇得到2米,从第三颗蘑菇得到3米,一共将获得7米蘑菇。
提莫师兄本着保护环境,绿色发展的原则,所以他不会砍掉过多的蘑菇。因此需要尽可能高地设定锯片高度。然后提莫师兄很懒,不想动脑计算,于是把你拉了过来帮他解决问题,找到砍伐机锯片的最大的整数高度H,使得提莫师兄可以得到的蘑菇至少有M米。换句话说,如果再升高1米,就得不到M米蘑菇。
输入格式
第一行输入俩个整数,第一个表示蘑菇的个数n(n<=1000000)需要的锯下来的蘑菇的总高度h(保证不大于int的最大值)。
第二行表示n个蘑菇的高度(保证所有高度加起来大于等于h)。
输出格式
请输出最合适的锯片的高度H
题目来源:
https://www.luogu.com.cn/training/128274
代码:
n, h = list(map(int, input().split()))
a = list(map(int, input().split()))
def judge(high):
sum = 0
for i in a:
if i - high > 0:
sum += i - high
if sum < h:
return True
return False
left = 0
right = max(a)
while left <= right:
mid = (left + right) // 2
if judge(mid):
right = mid - 1
else:
left = mid + 1
print(right)
深度优先搜索
def dfs(要进行递归的当前状态变量):
标记当前状态变量已经被访问
if 到达了问题空间的边界:(递归边界)
#已经搜索完了问题空间的一个分支
#进行回退之前的相应处理
更新目标变量
return
for 当前状态变量所连接的所有其余状态变量:(递归体)
标记这个连接的其余状态变量的状态为已经访问
dfs(这个其余状态变量)
#回退,取消标记点,方便其它的与这个状态变量相连的
#变量能够进行连接
标记这个连接的其余状态变量的状态为没有被访问
广度优先搜索
import collections
q = collections.deque()
def bfs (开始状态):
将开始状态加入队列
while 队列非空:
当前状态 = 取出队列队首元素
for 从当前状态 到 所有可达的状态:
if 可达且未被访问:
XXX处理
加入队列
并查集
def find(x):
if x == f[x]:
return x
f[x] = find(f[x])
return f[x]
def join(x, y):
fx = find(x)
fy = find(y)
if fx != fy:
f[fy] = fx
n, m, p = list(map(int, input().split()))
# init
f = [i for i in range(0, n+1)]
for i in range(m):
x1, x2 = list(map(int, input().split()))
join(x1, x2)
for i in range(p):
x1, x2 = list(map(int, input().split()))
if find(x1) == find(x2):
print('Yes')
else:
print('No')
最小生成树和单源最短路径
等待更新Python版,客官们先看C艹版
https://blog.csdn.net/weixin_45766049/article/details/114638079
最大连续子序列和
给定一个数字序列A1,A2,An,求i,j(1<=i<=j<=n),使得Ai+…+Aj最大
解题思路:
令状态dp[i]表示以A[i]作为末尾的连续序列最大的和
则问题转化为求dp数组的最大值
对于dp[i]的值,只有两种情况
1.这个最大和的连续序列只有一个元素,即A[i](dp[i]=A[i])
2.这个最大和的连续序列有多个元素,即A[p]+...+A[i](dp[i-1]+A[i])
综上,状态转移方程为
dp[i]=max{A[i],dp[i-1]+A[i]}
代码实现:
#最大值
maxn=10001
#数字序列
A=[0 for i in range(maxn)]
#状态序列
dp=[0 for i in range(maxn)]
#数字数量
n=int(input())
for i in range(n):
A[i]=int(input())
#边界
dp[0]=A[0]
#状态转移
for i in range(n):
dp[i]=max(A[i],dp[i-1]+A[i])
#获取状态最大值
k=0
for i in range(n):
if dp[i]>dp[k]:
k=i
最长不下降子序列
在一个数字序列中,找到一个最长的子序列,使得这个子序列是不下降的
解题思路:
令dp[i]表示以A[i]结尾的最长不下降子序列长度
对于dp[i]的值,只有两种情况
1.如果存在A[i]之前的元素A[j],使得A[j]<=A[i],且dp[j]+1>dp[i],则
把A[i]跟在以A[j]结尾的最长不下降子序列后,形成一条更长的不下降子序列
dp[i]=dp[j]+1
2.如果A[i]之前的元素都比A[i]大,则A[i]自成一条最长不下降子序列,dp[i]=1
则状态转移方程为:
dp[i]=max{1,dp[j]+1}
代码实现:
#数字个数
n=int(input())
#数字序列
A=[0 for i in range(n)]
#状态序列
dp=[0 for i in range(n)]
for i in range(n):
A[i]=int(input())
#最大的不下降序列标号
ans=-1
#状态转移
for i in range(1,n+1,1):
#边界,先假设每个元素自成一个子序列
dp[i]=1
for j in range(1,i+1,1):
if A[i]>=A[j] and dp[j]+1>dp[i]:
dp[i]=dp[j]+1
ans=max(ans,dp[i])
最长公共子序列
给定两个字符串A,B求一个字符串,使得这个字符串是A和B的最长公共部分。
解题思路:
令dp[i][j]表示字符串A的i号位置和字符串B的j号位置之前的最长公共子序列
对于dp[i][j]的值,只有两种情况
1.若A[i]==B[j],则字符串A和字符串B的最长公共子序列增加了一位,dp[i][j]=dp[i-1][j-1]+1
2.若A[i]!=B[j],则字符串A和字符串B的最长公共子序列无法延长,dp[i][j]=max(dp[i-1][j],dp[i][j-1])
综上,状态转移方程为:
dp[i][j]=dp[i-1][j-1] 当A[i]==B[j]
dp[i][j]=max(dp[i-1][j],dp[i][j-1]) 当A[i]!=B[j]
代码实现:
#数据个数
n=int(input())
#字符串A
A=''
#字符串B
B=''
#状态数组
dp=[[0]*n for i in range(n)]
#输入字符串
A=input()
b=input()
lenA=len(A)+1
lenB=len(B)+1
#边界
for i in range(lenA+1):
dp[i][0]=0
for j in range(lenB+1):
dp[0][j]=0
#状态转移
for i in range(1,lenA+1):
for j in range(1,lenB+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[lenA][lenB])
最长回文子串
给定一个字符串s,求s的最长回文子串的长度
解题思路:
令dp[i][j]表示s[i]到s[j]表示的子串是否是回文子串,是赋值为1,否则为0
则dp[i][j]的取值只有两种情况
1.若s[i]==s[j],那么只要s[i+1]到s[j-1]是回文子串,s[i]到s[j]就是回文子串
2.若s[i]!=s[j],那么s[i]到s[j]肯定不是回文子串
综上,状态转移方程为:
dp[i][j]=dp[i+1][j-1] 当s[i]==s[j]
dp[i][j]=0 当s[i]!=s[j]
代码实现:
maxn=1010
#字符串
s=input()
#状态列表
dp=[[0]*maxn for i in range(maxn)]
lens=len(s)
#回文字符串长度
ans=1
#边界
for i in range(lens):
dp[i][i]=1
if i<len-1:
if s[i]==s[i+1]:
dp[i][i+1]=1
ans=2
#状态转移
#子串长度
for l in range(3,lens+1):
#左端点
for i in range(lens-l+1):
#右端点
j=i+l-1
if s[i]==s[j] and dp[i+1][j-1]==1:
dp[i][j]=1
ans=l
背包问题
0-1背包和完全背包的不同:
从二维数组上区别0-1背包和完全背包也就是状态转移方程就差别在放第i中物品时,完全背包在选择放这个物品时,最优解是F[i][j-c[i]]+w[i]即画表格中同行的那一个,而0-1背包比较的是F[i-1][j-c[i]]+w[i],上一行的那一个。
从一维数组上区别0-1背包和完全背包差别就在循环顺序上,0-1背包必须逆序,因为这样保证了不会重复选择已经选择的物品,而完全背包是顺序,顺序会覆盖以前的状态,所以存在选择多次的情况,也符合完全背包的题意。状态转移方程都为F[i] = max(F[i],dp[F-c[i]]+v[i])
0-1背包
有n件物品,每件物品重量为w[i],价值为c[i],现有一个容量为V的背包,问如何选取物品放入背包,使得背包内物品的总价值最大,其中每种物品都只有1件
解题思路:
令dp[i][v]为前i件物品恰好放入容量为v的背包中所能获得的最大价值
对于dp[i][v]只有两种情况
1.不放第i件物品,那么问题转化为前i-1件物品恰好装入容量为v的背包中
所能获得的最大价值,即dp[i-1][v]
2.放入第i件物品,那么问题转化为前i-1件物品恰好放入容量为v-w[i]的背包
中所能获得的最大价值,dp[i-1][v-w[i]]+c[i]
综述,状态转移方程为
dp[i][v]=max(dp[i-1][v],dp[i-1][v-w[i]]+c[i])
代码实现:
for i in range(n+1):
for v in range(w[i],V+1):
dp[i][v]=max(dp[i-1][v],dp[i-1][v-w[i]]+c[i])
#采用滚动数组,压缩空间
#物品最大件数
maxn=100
#最大容量
maxv=1000
#物品重量
w=[0 for i in range(maxn)]
#物品价值
c=[0 for i in range(maxn)]
#对于第i件物品,容量为k时的最大价值
dp=[0 for i in range(maxv)]
#物品件数
n=int(input())
#背包容量
V=int(input())
#输入数据
for i in range(1,n+1):
w[i]=int(input())
for i in range(1,n+1):
c[i]=int(input())
#边界
for v in range(1,V+1):
dp[v]=0
#状态转移
for i in range(1,n+1):
for v in range(V,w[i]-1,-1):
dp[v]=max(dp[v],dp[v-w[i]]+c[i])
print(max(dp))
多重背包(二进制优化)
# C++版
# for(int i=1;i<=n;i++)
# {
# int numm=num[i];//num数组存放的是第i个物品的数目;除了加了个二进制优化外其他的和01背包完全一样
# for(int j=1;numm>0;j<<1)
# {
# j=min(j,numm);
# for(int k=m;k>=j*w[i];k--)
# {
# dp[k]=max(dp[k],dp[k-j*w[i]]+j*value[i]);
# }
# numm-=j;
# }
# }
for i in range(1, n+1):
numm = num[i]
j = 0
while numm > 0:
j = min(j, numm)
for k in range(m, j*w[i]-1, -1):
dp[k] = max(dp[k], dp[k-j*w[i]]+j*value[i])
numm -= j
j <<= 1
完全背包
代码实现:
# 一维数组
for i in range(n+1):
for j in range(w[i],V+1):
# 注意此处,与0-1背包不同,这里为顺序,0-1背包为逆序
dp[j]=max(dp[j],dp[j-w[i]]+c[i]);
参考文献
[1] oldsummer 刷题常用算法(Python实现) https://zhuanlan.zhihu.com/p/363712120
[2] 各种B乎
[3] 各种CSDN