day2
1.队列
from queue import Queue
q = Queue(maxsize=0)#maxsize设置队列中数据上限,小于或等于0则不限制;容器中大于这个数则阻塞,直到队列中的数据被消掉
Queue.qsize()
返回队列的大致大小。
Queue.empty()
如果队列为空,返回 True
否则返回 False
Queue.full()
如果队列是满的返回 True
,否则返回 False
Queue.put(item, block=True, timeout=None)
- 常用时忽略默认参数,即使用
Queue.put(item)
。
Queue.get(block=True, timeout=None)
- 常用时忽略默认参数,即使用
Queue.get()
。
Python 中的队列线程安全,太慢了,所以我们一般用deque (双端队列)。它是Python内置的一个高效的序列数据结构,属于collections
模块。它支持从两端快速添加(append)和删除(pop)元素,比如你可以用它来实现栈(stack)和队列(queue)。deque
是双端队列的缩写,代表它可以从头部或尾部添加或删除元素,而操作的时间复杂度都是O(1)。
from collections import deque
d = deque()#创建一个deque对象
d = deque([1, 2, 3, 4])#也用一个可迭代对象初始化deque
d.append(5) # d 现在是 deque([1, 2, 3, 4, 5])
d.appendleft(0) # d 现在是 deque([0, 1, 2, 3, 4, 5])
d.pop() # 返回 5, d 现在是 deque([0, 1, 2, 3, 4])
d.popleft() # 返回 0, d 现在是 deque([1, 2, 3, 4])
2.字典
#创建字典
massege={'小李':'123124543643','xiaohua':'17855666','LiMing':'1249699859456'}
#或者创建空的字典
empty_dict = {}
#或者使用元组作为key
group_dict = {(60, 99):'good', 100:'nice'}
# 如果字典内不含有相应的Key值,则会执行添加操作
dict[key]=value
# 如果字典内含有相应的Key值,则会执行更新操作
dict[key]=new_value
# 使用update()修改
# update() 方法可使用一个字典所包含的 key-value 对来更新己有的字典。如果有就修改,没有就添加。
dict.update({'key':123,'key2':234})
del dict['key'] # 删除键是'key'的条目
dict.clear() # 清空字典所有条目
del dict # 删除字典
dict = {'Name': 'Zara', 'Age': '7'}
print (dict['Name'])#当然如果key值不存在,将会抛出异常
#也可以是用get()方法,不存在会返回None,但不会抛出异常
print(dict.get('Name'))
3.集合
# 1. set 的定义 变量名=set()
intSet=set()
stringSet=set()
s1 = "测试1"
s2 = "测试2"
s3 = "测试3"
# 2. 插入操作
stringSet.add(s3)
stringSet.add(s2)
#5.返回集合元素数量
print("前2次插入操作完成后的元素数量为%d\n",len(stringSet))
stringSet.add(s1)
print("前3次插入操作完成后的元素数量为%d\n",len(stringSet))
#6.遍历整个集合
for ele in stringSet:
print(ele,end=' ')
print()
#3. 删除操作
stringSet.discard(s3)
print("删除操作完成后的元素数量为%d\n",len(stringSet))
for ele in stringSet:
print(ele,end=' ')
print()
# 4. 判断是否由此元素
if s2 in stringSet:
print("存在元素")
前2次插入操作完成后的元素数量为%d
2
前3次插入操作完成后的元素数量为%d
3
测试1 测试2 测试3
删除操作完成后的元素数量为%d
2
测试1 测试2
存在元素
day3
1.简单枚举
ans = [[] for i in range(10)]
a = ['0']*10
if __name__ == '__main__':
c = input().split()
for i in range(6):
if c[i] == 'A':
a[i] = 1
elif c[i] == 'J':
a[i] = 11
elif c[i] == 'Q':
a[i] = 12
elif c[i] == 'K':
a[i] = 13
else:
a[i] = ord(c[i]) - ord('0')
ans[0].append(a[0])
for i in range(1, 6):
for j in range(len(ans[i - 1])):
ans[i].append(ans[i - 1][j] + a[i])
ans[i].append(ans[i - 1][j] - a[i])
ans[i].append(ans[i - 1][j] * a[i])
ans[i].append(int(ans[i - 1][j] / a[i]))
flag = 0
for j in range(len(ans[5])):
if ans[5][j] == 42:
flag = 1
break
if flag == 1:
print("YES")
else:
print("NO")
2.组合问题
chosen=[]
m=0
n=0
s=''
name=[]
ans=[]
def calc(x):#组合问题(n个数选m个)函数表示选择到第x个数了
ansTem=''
if len(chosen)>m or (len(chosen)+(n-x+1))<m:#当选择的数量大于m(表示选够了),或者选择的数量+剩余的数量n-x+1<m表示不够选了。则可以剪枝
return
elif x==n+1:#当选到n+1个时候,表示n个数已经选完了,打印结果
# for i in range(len(chosen)):
# ansTem+=name[chosen[i]-1]+' '
# ans.append(ansTem)
# return
for i in chosen:
ansTem = ansTem + name[i - 1] + ' '#数是从1开始选的,而数组下表从0开始,形成映射
# print(ansTem)
ans.append(ansTem)
return
#每个数都有两种情况,即选或者不选
chosen.append(x)#选第x个数
calc(x+1)#处理下一个数
chosen.pop()#不选选第x个数
calc(x+1)
n,m=map(int,input().split())
for i in range(n):
s=input()
name.append(s)
calc(1)
for i in range(len(ans)):
print(ans[i])
手写子集(二进制法)
a=[1,2,3,4,5,6,7,8,9,10,11,12,13,14]
def print_subset(n):#求子集
for i in range(1 << n):#即0-2**n-1共2**n个子集
for j in range(n):#遍历1、10、100
if (i & (1 << j) !=0):#按位与为1则找到子集元素
print(a[j],end=" ")
print()#一个子集结束
print(print_subset(3))#求n=3的集合{1,2,3}的子集
3.排列问题
手写全排列
chosen=[0]*20#判断选过与否
order=[0]*20#存储已经排好序的
s=''
name=[]
ans=[]
def calc(k):#处理排在k位置数
ansTem=""
if k==n+1:#位置来到k+1则处理完毕一组,打印
for i in range(1,n+1):
ansTem+=name[order[i]-1]+" "
print(ansTem)
return
for i in range(1,n+1):
if chosen[i]==1:#如果该数选过了,就选下一个
continue
order[k]=i#没选过就选上
chosen[i]=1#标记选过
calc(k+1)#下一个位置
order[k]=0#不选该数了
chosen[i]=0#取消标记
n=int(input())
for i in range(n):
s=input()
name.append(s)
calc(1)
在python中有一个强大的函数permutation,它可以完成全排列。
功能:连续返回由iterable序列中的元素生成的长度为r的排列
如果未指定r或者r为None,则r为iterable的长度,即生成包含所有元素的全排列
例如:
上图中左侧为输出
#输入一串字符串,求出该字符串在该字符串全排列之后的序号。(初始序号为0)
from itertools import*
cnt=0
string=input()
a=list(string)
a.sort()
for i in permutations(a):
a=''.join(i)
if string==a:
print(cnt)
break
else:
cnt+=1
给出N个数的排列,输出这个排列后面的第M个排列
输入样例
5
3
1 2 3 4 5
输出样例
1 2 4 5 3
#permutations()函数是按元素位置进行排列输出的,即排序成最小列,然后从最小列开始permutations(),遇到给定的排列再往后数m个排列,输出。这会超时。
#手写下一个排列:从当前排列开始暴力的寻找下一个排列。对于当前排列,从后往前比较,寻找nums[i-1]<nums[i]的位置,把nums[i-1]与i到末尾中比nums[i-1]大的最小数交换,再将i-1之后的数进行翻转,可以得到比当前排列大的最小排列。
n=int(input())
m=int(input())
nums=list(map(int,input().split()))
def find(nums):
for i in range(n-1,0,-1):
if nums[i-1]<nums[i]:
for j in range(n-1,i-1,-1):
if nums[j]>nums[i-1]:
nums[j],nums[i-1]=nums[i-1],nums[j]
return nums[:i] + nums[:i-1:-1]
for i in range(m):
nums=find(nums)
print(" ".join([str(i) for i in nums]))
数组的处理
4.尺取法
回文数判断
s=list(input())
i=0
j=len(s)-1
if i==j:
print("YES")
else:
while s[i]==s[j]:
i+=1
j-=1
if j<=i:
print("YES")
break
else:
print("NO")
输入n个数,放在数组a[]中。找出其中两个数,他们之和为m
n,m=map(int,input().split())
s=[]
for i in range(n):
a=input()
s.append(int(a))
s.sort()
i=0
j=len(s)-1
if i==j:
print("NO")
else:
while s[i]+s[j]<m:
i+=1#小的变大
if j<=i:
print("NO")
break
while s[i]+s[j]>m:
j-=1#大的变小
if j<=i:
print("NO")
break
else:
print(s[i],s[j])
美丽区间
n,s=map(int,input().split())
sum=0
ans=10**8
a=list(map(int,input().split()))
i=0#右指针
j=0#左指针
while i < n:#遍历整个列表
if sum<s:
sum+=a[i]
i+=1#和不够,则右指针右移
else:
ans=min(i-j,ans)#记录最小长度
sum-=a[j]
j+=1#和过多,则左指针右移
if ans==10**8:
print('0')
else:
print(ans)
day4
1.递推与递归
#斐波那契数列
#1.递推
n=int(input())
x,y=0,1
ans=0
if n==0:
ans=0
elif n==1:
ans=1
else:
for i in range(n-1):
ans=x+y
x=y
y=ans
print(ans)
#2.递归
n=int(input())
def f(n):
if n==0:
return 0
elif n==1:
return 1
else:
return f(n-2)+f(n-1)
print(f(n))
2.存储型的递推与递归
#1.递推
m=int(input())
F=[0]*35
F[0]=0
F[1]=1
for i in range(2,31):
F[i]=F[i-1]+F[i-2]
for i in range(m):
n=int(input())
print(F[n])
#2.递归
m=int(input())
F=[0]*35
def f(n):
if n==0:
F[0]=0
return 0
elif n==1:
F[1]=1
return 1
else:
F[n]=f(n-1)+f(n-2)
return F[n]
f(30)
for i in range(m):
n=int(input())
print(F[n])
数字三角形
n=int(input())
a=[[0]*101]*101
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(max(a[1]))
数字三角形(向左下走的次数与向右下走的次数差不大于1)
n=int(input())
a=[[0]*101]*101
for i in range(1,n+1):
a[i]=input().split()
a[i]=list(map(int,a[i]))
for i in range(2,n+1):
for j in range(0,i):
if j==0:
a[i][j]+=a[i-1][j]
elif j==i-1:
a[i][j]+=a[i-1][j-1]
else:
a[i][j]+=max(a[i-1][j-1],a[i-1][j])
if n%2==0:
print(max(a[n][(n//2)-1],a[n][(n//2)]))
else:
print(a[n][(n//2)])
数的划分
n,k=map(int,input().split())
#dp=[[0]*210]*211,这种写法不对,改一行,其他行也会改变
dp=[[0]*210 for i in range(211)]#dp[i][j]表示整数i分为j份的分法数
for i in range(1,n+1):
dp[i][1]=1#整数i分为1份,显然只有一种分法
dp[i][i]=1#整数i分为i份,显然也只有一种分法
for i in range(3,n+1):#dp[1][1]\dp[2][1]\dp[2][2]\dp[3][1]都已经初始化,从dp[3][2]开始
for j in range(2,k+1):
if i>j:#整数i显然必须大于分数,否则不合理(如3如何分为4份?)
dp[i][j]=dp[i-j][j]+dp[i-1][j-1]#每种情况分为两种情况(1.先拿一个给一份,剩下的分为k-1份。2.先拿k个分别拿一个分给k份,剩下的i-k再分为k份)两种情况和为总情况
print(dp[n][k])
数的计算
def f(n):#递归的枚举每种情况
global res
if n==1:#n为1,只有一个数即1
return
for i in range(1,n//2+1):#枚举左边的所有情况
res+=1#满足条件加1
f(i)#每个情况自己左边也可以枚举
res=1
n=int(input())
f(n)
print(res)
day5
1.DFS
N皇后问题
x=[0]*15#存放第i个放置皇后的列号
sum=0#有几种放法
n=0
def pd(k):#判断前k个皇后与第K个皇后的位置是否冲突
for i in range(1,k):
if abs(x[i]-x[k])==k-i:#列差与行差相等,那么斜率为正负1,出现在斜线上,不符合题意
return 0
elif x[i]==x[k]:#列号不能相同
return 0
return 1
def check(a):
global sum
if a>n:#放到第n+1个,显然前n个放完了,方法数+1
sum+=1
else:
return 0
return 1
def DFS(a):
if check(a):#已经放了n个皇后了,回溯,找下一种方法
return
else:
for i in range(1,n+1):#遍历每一列
x[a]=i
if pd(a):#能发就找下一行
DFS(a+1)
else:#不能就找下一列
continue
n=int(input())
DFS(1)
print(sum)
路径之谜
flag=[[0 for i in range(25)] for j in range(25)]#标记数组,判断坐标(i,j)是否走过
flag[1][1]=1
resx=[0 for i in range(1000)]#存储路径
resy=[0 for i in range(1000)]
resx[0]=1#存储起点
resy[0]=1
resCount=1#该走第几步了
xyid=0#坐标点的序号
n=int(input())
y=input().split()#先读入y轴箭靶上的箭数(北面箭靶)
x=input().split()
xj=list(map(int,x))
yj=list(map(int,y))
yj=[0]+yj
yj[1]-=1
xj=[0]+xj
xj[1]-=1
dx=[1,-1,0,0]#走的方向,可以向上、下、左、右四个方向走
dy=[0,0,1,-1]
def pd(x,y):#判定函数,判断这条路是否走过,是否出界了,是否还有剩余的箭
if flag[x][y]==1 or x<1 or x>n or y<1 or y>n or xj[x]<=0 or yj[y]<=0:
return 0
else:
return 1
def check(x,y):#检查函数,判断是否走到终点了
if x==n and y==n:
if (sum(xj)+sum(yj))!=0:#箭靶上还有箭,表示路径走的不对
return 0
else:#箭靶上没有箭,打印路径
for i in range(resCount):
xyid=(resx[i]-1)*n+resy[i]-1
print(xyid,end=' ')
return 1
else:
return 0
def dfs(x,y):
global resCount
if check(x,y):#走到终点了,结束
return
else:#否则继续走
for i in range(4):#四个方向
xt=x+dx[i]
yt=y+dy[i]
if pd(xt,yt):#判定函数,看能不能走,能走就走
resx[resCount]=xt#更新坐标
resy[resCount]=yt
flag[xt][yt]=1#标记路径
resCount+=1#更新步数
xj[xt]-=1#拔箭
yj[yt]-=1
dfs(xt,yt)#走下一步
resCount-=1#回溯,步数回退
xj[xt]+=1#把箭插回去
yj[yt]+=1
flag[xt][yt]=0#取消标记
else:#不能走就换方向
continue
dfs(1,1)
最大数字
n,a,b=map(int,input().split())
s=str(n)
ans=0
def dfs(i,sum_,a,b):#搜到第i个数时,此时累计的和(sum_*10就是此位数之前的位数和)
global ans,s
if i==(len(s)):#每一位都操作了,更新ans,取所有方案的最大值
ans=max(ans,sum_)
return
x=int(s[i])#取出当前数位值
t=min(9-x,a)#要么操作9-x次使之变为9,要么操作a次使之变大
dfs(i+1,sum_*10+x+t,a-t,b)#操作位数+1,此时累计的和+t,a操作-t
if (x+1)>b:#b操作减小数值,将一个数变为9需要x+1次,不够就放弃操作,直接找下一位
dfs(i+1,sum_*10+x,a,b)
else:
dfs(i+1,sum_*10+9,a,b-(x+1))#够便操作位数+1,此时累计的和+9,b操作x+1次
dfs(0,0,a,b)
print(ans)
2.BFS
长草
from collections import deque
q=deque()
dx=[1,-1,0,0]
dy=[0,0,1,-1]
n,m,k=0,0,0
n,m=map(int,input().split())
Map=[[0 for i in range(m)] for j in range(n)]
def pd(x,y):
if x<0 or x>=n or y<0 or y>=m or Map[x][y]=='g':
return 0
else:
return 1
def bfs():
global m,n,k,Map,q,length
while k>0 and len(q)>0:
temp=q.popleft()
x=temp[0]
y=temp[1]
for i in range(4):
xt=x+dx[i]
yt=y+dy[i]
if not pd(xt,yt):
continue
else:
Map[xt][yt]='g'
q.append((xt,yt))
length-=1
if length==0:
k-=1
length=len(q)
for i in range(n):
input_=input()
for j in range(m):
Map[i][j]=input_[j]
if Map[i][j]=='g':
q.append((i,j))
length=len(q)
k=int(input())
bfs()
for i in range(n):
s=''
for j in range(m):
s+=Map[i][j]
print(s)
用数组模拟队列,效率更高
n,m,k=0,0,0
q=[]
qfont,qend=0,0
dx=[1,-1,0,0]
dy=[0,0,1,-1]
def pd(x,y):
if x<0 or x>=n or y<0 or y>=m or Map[x][y]=='g':
return 0
else:
return 1
def BFS():
global k,m,n,Map,length,q,qfont,qend
while k>0 and length>0:
temp=q[qfont]
qfont+=1
x=temp[0]
y=temp[1]
for i in range(4):
xt=x+dx[i]
yt=y+dy[i]
if pd(xt,yt):
q.append((xt,yt))
qend+=1
Map[xt][yt]='g'
else:
continue
length-=1
if length==0:
k-=1
length=qend-qfont
n,m=map(int,input().split())
Map=[[0 for i in range(m)] for j in range(n)]
for i in range(n):
input_=input()
for j in range(m):
Map[i][j]=input_[j]
if Map[i][j]=='g':
q.append((i,j))
qend+=1
length=qend-qfont
k=int(input())
BFS()
for i in range(n):
s=''
for j in range(m):
s+=Map[i][j]
print(s)
走迷宫
q=[]
qfont,qend=0,0
n,m,xs,ys,xe,ye=0,0,0,0,0,0
n,m=map(int,input().split())
G=[[0 for i in range(m+10)] for j in range(n+10)]
vis=[[0 for i in range(m+10)] for j in range(n+10)]
dx=[0,0,1,-1]
dy=[1,-1,0,0]
ans=-1
for i in range(n):
input_=input().split()
for j in range(m):
G[i+1][j+1]=input_[j]
xs,ys,xe,ye=map(int,input().split())
vis[xs][ys]=1
q.append((xs,ys))
qend+=1
def pd(x,y):
if vis[x][y]!=0 or x<1 or x>n or y<1 or y>m or G[x][y]!='1':
return 0
else:
return 1
def check(x,y):
global ans
if x==xe and y==ye:
ans=vis[x][y]-1#题目案例没算起点
return 1
else:
return 0
def bfs():
global qfont,qend
while (qend-qfont)!=0:
temp=q[qfont]
qfont+=1
x=temp[0]
y=temp[1]
if check(x,y):
return
else:
for i in range(4):
xt=x+dx[i]
yt=y+dy[i]
if pd(xt,yt):
vis[xt][yt]=vis[x][y]+1
q.append((xt,yt))
qend+=1
else:
continue
bfs()
print(ans)
迷宫
from collections import deque
q=deque()
q.append((0,0))
n=30
m=50
Map=[[int(i) for i in input()] for j in range(n)]
vis=[[0 for i in range(m)] for j in range(n)]
go=[['' for i in range(m)] for j in range(n)]
vis[0][0]=1
while q:
x,y=q.popleft()
if x==29 and y==49:
print(go[-1][-1])
break
else:
for i,j,k in [[1,0,'D'],[0,-1,'L'],[0,1,'R'],[-1,0,'U']]:
a=x+i
b=y+j
if 0<=a<n and 0<=b<m and vis[a][b]==0 and Map[a][b]==0:
vis[a][b]=vis[x][y]+1
go[a][b]=go[x][y]+k
q.append((a,b))
day6
1.差分法
2.前缀和
day7
1.并查集
普通合并
def init():
for i in range(10**6+10):
fa.append(i)
def find(x):
if fa[x]==x:
return x
else:
fa[x]=find(fa[x])
return fa[x]
def merge(x,y):
fa[find(x)]=find(y)
ans=0
fa=[]
init()
m,n=map(int,input().split())
k=int(input())
for i in range(k):
a,b=map(int,input().split())
merge(a,b)
for i in range(1,m*n+1):
if fa[i]==i:
ans+=1
print(ans)
优化合并
def init():
for i in range(10**6+10):
fa.append(i)
rk.append(1)#初始化连通分量的深度为1
def find(x):
if fa[x]==x:
return x
else:
fa[x]=find(fa[x])
return fa[x]
def merge(x,y):
x=find(x)
y=find(y)
if x!=y:
if rk[x]>rk[y]:
x,y=y,x#保证y的深度始终大于x
fa[x]=y#将x合并到y上
if rk[x]==rk[y]:
rk[y]+=1
ans=0
fa=[]
rk=[]
init()
m,n=map(int,input().split())
k=int(input())
for i in range(k):
a,b=map(int,input().split())
merge(a,b)
for i in range(1,m*n+1):
if fa[i]==i:
ans+=1
print(ans)
修改数组
暴力法
n=int(input())
a=list(map(int,input().split()))
b=set()
for i in range(n):
while a[i] in b:
a[i]=a[i]+1
b.add(a[i])
for i in range(n):
print(a[i],end=' ')
并查集
fa=[0 for i in range(10**6+10)]
def go():
for i in range(10**6+10):
fa[i]=i
def find(x):
if fa[x]==x:
return x
else:
fa[x]=find(fa[x])
return fa[x]
n=int(input())
a=list(map(int,input().split()))
go()
for i in range(n):
#当a[i]没出现过,显然父亲就是自己,a[i]不变;当a[i]出现过,a[i]用父亲更新保证更新不重复
x=find(a[i])#将a[i]的父亲赋给x,再用x更新a[i]
a[i]=x
fa[a[i]]=find(x+1)#a[i]父亲为x+1,即比自己大1的值,因为大1的值也可能出现过,所以找x+1的父亲
for i in range(n):
print(a[i],end=" ")
2.并查集撤销操作
蓝桥地图
fa=[]
rk=[]
undo_fa=[]
undo_rk=[]
class UndoObject:
def __init__(self,pos,val):
self.pos=pos
self.val=val
def go():
for i in range(10**5+10):
fa.append(i)
rk.append(1)
def find(x):
if fa[x]==x:
return x
else:
return fa[x]
def merge(x,y):
x=find(x)
y=find(y)
if x!=y:
if rk[x]>rk[y]:
x,y=y,x
undo_fa.append(UndoObject(x,fa[x]))
fa[x]=y
undo_rk.append(UndoObject(y,rk[y]))
if rk[x]==rk[y]:
rk[y]+=1
def undo():
undo_fa_top=undo_fa.pop()
fa[undo_fa_top.pos]=undo_fa_top.val
undo_rk_top=undo_rk.pop()
rk[undo_rk_top.pos]=undo_rk_top.val
n,m=map(int,input().split())
go()
for i in range(m):
op,u,v=map(int,input().split())
if op==1:
if find(u)!=find(v):
merge(u,v)
else:
undo()
if op==2:
if find(u)!=find(v):
print("N")
else:
print("Y")
注:上题用并查集撤销操作只能过40%弱数据,有如下例子:
#一开始都未联通
1 u1 v1
1 u2 v2
1 u1 v1
以上操作先将u1 v1联通,再将u2 v2联通,注意最后的操作,因为u1 v1已经联通,我们希望将u1 v1断开,可是并查集撤销操作会将最近一次的操作撤销即u2 v2会断开。因此需要用到线段树时间分治的知识来解决,感兴趣的读者可以自行搜索。
day8
1.二分法
模板
#找>=x的最小值
while low<height:
mid=(low+height)//2
if a[mid]>=x:
height=mid
else:
low=mid+1
#找<=x的最大值
while low<height:
mid=(low+height+1)//2
if a[mid]<=x:
low=mid
else:
height=mid-1
2.整数二分
L,N,M=map(int,input().split())
l=0
r=L
a=[0 for i in range(N)]
for i in range(N):
a[i]=int(input())
a=[0]+a+[L]
def check(d):#检查当前的最小距离是否能够在移动M次内找到,不能就减小d能就加大d
num=0
pos=0
for i in range(1,N+2):
if a[i]-pos<d:
num+=1
else:
pos=a[i]
if num<=M:
return 1
else:
return 0
while l<r:#<=x的最大值模板
mid=(l+r+1)//2
if check(mid):
l=mid
else:
r=mid-1
print(r)
分巧克力
n,k=map(int,input().split())
hi=[]
wi=[]
for i in range(n):
h,w=map(int,input().split())
hi.append(h)
wi.append(w)
def check(d):#检查当前边长是否能够分给k个人,能就加大d,不能就减小d
sum_=0
for i in range(n):
sum_+=(hi[i]//d)*(wi[i]//d)
if sum_>=k:#提前判断第i个巧克力之前切割后的总和够不够分,不用等到所有巧克力切割完再判断
return 1
return 0
l=0
r=max(max(hi),max(wi))
while l<r:#<=x的最大值模板
mid=(l+r+1)//2
if check(mid):
l=mid
else:
r=mid-1
print(l)
3.实数二分
模板
eps=1e-6
while (l+eps)<r:
mid=(l+r)/2
if pd():
r=mid#实数二分无需考虑边界问题
else:
l=mid
M次方根
n,m=map(int,input().split())
def pd(a,m):#求a的m次幂,如果大于n,显然大于a的所有值的m次幂必然大于n,因此考虑(l,mid)否则考虑(mid,r)
cnt=m
total=1
while cnt>0:
total*=a
cnt-=1
if total>=n:
return 1
else:
return 0
eps=1e-8
l=0
r=n
while (l+eps)<r:
mid=(l+r)/2
if pd(mid,m):
r=mid
else:
l=mid
print("%.7f"%l)
day9
贪心算法
常见的贪心问题
找零问题
t=[100,50,20,5,1]
n=int(input())
def change(t,a):
m=[0 for i in range(len(t))]
for pos,val in enumerate(t):
m[pos]=a//val
a%=val
return m
m=change(t,n)
for i in range(len(m)):
print(t[i],end=":")
print(m[i])
小B同学宿舍
#尽可能使搬运同时进行,以便使单独安排的搬运次数最少。这样用的时间最少,即所用最少时间为不能同时搬运的次数,即某一段走廊使用次数最多。
T=int(input())
s,t=0,0
while T>0:
ans=0
zl=[0 for _ in range(205)]
n=int(input())
for i in range(n):
s,t=map(int,input().split())
s=(s-1)//2
t=(t-1)//2
if s>t:
s,t=t,s
for j in range(s,t+1):
zl[j]+=1
ans=max(zl)
print(ans*10)
T-=1
可拆分背包问题之贪心的自助餐
class Food:
def __init__(self,w,v,dj):
self.w=w
self.v=v
self.dj=dj
n,C=map(int,input().split())
foods=[]
sum_=0
Value=0.0
for i in range(n):
food=Food(0,0,0)
food.v,food.w=map(int,input().split())
food.dj=food.v/food.w
foods.append(food)
foods.sort(key=lambda f:f.dj,reverse=True)
for i in range(len(foods)):
sum_+=foods[i].w
if sum_<=C:
for i in range(len(foods)):
Value+=foods[i].v
else:
for i in range(len(foods)):
if foods[i].w<=C:
C-=foods[i].w
Value+=foods[i].v
else:
Value+=foods[i].dj*C
C=0
if C==0:
break
print("%.3f"%Value)
快乐司机
n,w=map(int,input().split())
goods=[]
for i in range(n):
good=list(map(int,input().split()))
goods.append(good)
goods.sort(key=lambda x : x[1]/x[0],reverse=True)
sum_=0
Value=0
for i in range(n):
if goods[i][0]<=w:
Value+=goods[i][1]
w-=goods[i][0]
else:
Value+=(goods[i][1]/goods[i][0])*w
w=0
if w==0:
break
print("%.1f"%Value)
翻硬币
a=list(input())
b=list(input())
ans=0
for i in range(len(a)):
if a[i]!=b[i]:
if a[i+1]=='*':
a[i+1]='o'
else:
a[i+1]='*'
ans+=1
print(ans)
答疑
n=int(input())
goods=[]
for i in range(n):
stu=list(map(int,input().split()))
goods.append(stu)
goods.sort(key=lambda x:x[0]+x[1]+x[2])
# time=[0 for i in range(n+5)]
# time[1]=goods[0][0]+goods[0][1]
# for i in range(1,n):
# time[i+1]=time[i]+goods[i-1][2]+goods[i][0]+goods[i][1]
# print(sum(time))
time=0
ans=0
for i in goods:
time+=i[0]+i[1]
ans+=time
time+=i[2]
print(ans)
防御力
n1,n2=map(int,input().split())
A=list(map(int,input().split()))
B=list(map(int,input().split()))
C=list(input())
a=[]
b=[]
for i in range(len(A)):
a.append((i+1,A[i]))
a.sort(key=lambda x:x[1])
for i in range(len(B)):
b.append((i+1,B[i]))
b.sort(key=lambda x:x[1],reverse=True)
al=0
bl=0
for i in range(len(C)):
if C[i]=='0':
print('A',end='')
print(a[al][0])
al+=1
else:
print('B',end='')
print(b[bl][0])
bl+=1
print('E')
day10
动态规划
跳跃
#深度搜索
n,m=map(int,input().split())
a=[[0 for i in range(m+1)] for j in range(n+1)]
for i in range(n):
a[i+1]=input().split()
a[i+1]=[0]+list(map(int,a[i+1]))
dx=[1,2,3,0,0,0,1,2,1]
dy=[0,0,0,1,2,3,2,1,1]
ans=0
def dfs(x,y,w):
global ans
w=w
if x==n and y==m:
ans=max(ans,w)
return
else:
for i in range(9):
xt=x+dx[i]
yt=y+dy[i]
if xt<=n and yt<=m:
dfs(xt,yt,w+a[xt][yt])
dfs(1,1,a[1][1])
print(ans)
#动态规划
n,m=map(int,input().split())
a=[[0 for i in range(m+1)] for j in range(n+1)]
for i in range(n):
a[i+1]=input().split()
a[i+1]=[0]+list(map(int,a[i+1]))
dx=[1,2,3,0,0,0,1,2,1]
dy=[0,0,0,1,2,3,2,1,1]
for i in range(n,0,-1):
for j in range(m,0,-1):#难以确定前面的走法如何影响后面的走法,那么反过来推,倒推找到走到终点的最大值,前面的走法依赖后面的走法,从而确定起点的走法
if i==n and j==m:
continue
else:
temp=float('-inf')
for k in range(9):
xt=i+dx[k]
yt=j+dy[k]
if xt>n or yt>m:
continue
else:
temp=max(temp,a[xt][yt])
a[i][j]+=temp
print(a[1][1])
背包问题初始化细节
0/1背包
n,V=map(int,input().split())
w=[]#体积
v=[]#价值
dp=[0 for i in range(V+1)]
for i in range(n):
wi,vi=map(int,input().split())
w.append(wi)
v.append(vi)
for i in range(n):
for j in reversed(range(w[i],V+1)):
dp[j]=max(dp[j],dp[j-w[i]]+v[i])#0/1背包问题是根据上一行更新下一行,用自我滚动减小内存开销
print(dp[V])
完全背包
与0/1背包的区别就是每种物品有无数种,这时候我们只需要遍历容量J的时候由倒序转变为正序
n,V=map(int,input().split())
w=[]#体积
v=[]#价值
dp=[0 for i in range(V+1)]
for i in range(n):
wi,vi=map(int,input().split())
w.append(wi)
v.append(vi)
for i in range(n):
for j in range(w[i],V+1):#完全背包可以选无数个,遍历容量找到可以第i件物品在容量j时的最大价值(和0/1背包的遍历顺序正好相反)
dp[j]=max(dp[j],dp[j-w[i]]+v[i])
print(dp[V])
多重背包
# n,V=map(int,input().split())
# w=[]#体积
# v=[]#价值
# s=[]
# dp=[0 for i in range(V+1)]
# for i in range(n):
# wi,vi,si=map(int,input().split())
# w.append(wi)
# v.append(vi)
# s.append(si)
# for i in range(n):
# for k in range(1,s[i]+1):
# for j in reversed(range(w[i],V+1)):
# dp[j]=max(dp[j],dp[j-w[i]]+v[i])
# print(dp[V])
n,V=map(int,input().split())
w=[]#体积
v=[]#价值
s=[]
dp=[0 for i in range(V+1)]
for i in range(n):
wi,vi,si=map(int,input().split())
w.append(wi)
v.append(vi)
s.append(si)
for i in range(n):
for j in reversed(range(w[i],V+1)):
for k in range(1,s[i]+1):
if j-w[i]*k<0:
break
dp[j]=max(dp[j],dp[j-k*w[i]]+k*v[i])
print(dp[V])
dp=[[0 for i in range(2025)] for j in range(12)]
dp[0][0]=1
for i in range(1,2023):
for j in reversed(range(1,11)):
for k in range(i,2023):
dp[j][k]+=dp[j-1][k-i]
print(dp[10][2022])
过河卒
dp=[[0 for i in range(30)] for j in range(30)]
dp[2][1]=1
bx,by,mx,my=map(int,input().split())
bx+=2
by+=2
mx+=2
my+=2
flag=[[0 for i in range(by+2)] for j in range(bx+2)]
flag[mx-1][my-2]=1
flag[mx-2][my-1]=1
flag[mx+1][my-2]=1
flag[mx+2][my-1]=1
flag[mx-2][my+1]=1
flag[mx-1][my+2]=1
flag[mx+1][my+2]=1
flag[mx+2][my+1]=1
flag[mx][my]=1
for i in range(2,bx+1):
for j in range(2,by+1):
if flag[i][j]==1:
dp[i][j]=0
else:
dp[i][j]=dp[i-1][j]+dp[i][j-1]
print(dp[bx][by])
最长公共子序列.1
a=list(input())
b=list(input())
#dp[i+1][j+1]表示a以a[i]结尾,b以b[j]结尾的最大公共子序列的长度
#num[i+1][j+1]表示a以a[i]结尾,b以b[j]结尾的最大公共子序列的个数
#1、如果a[i+1]=b[j+1],那么个数就可以直接继承前面的,因为最后这个字符肯定要用,num[i+1][j+1]=num[i][j]
#2、dp[i+1][j+1]=dp[i][j+1] 同样的转移 num[i+1][j+1]+=num[i][j+1]
#3、dp[i+1][j+1]=dp[i+1][j] num[i+1][j+1]+=num[i+1][j]
#4、如果a[i]!=b[j],那计算dp数组,肯定是max(dp[i+1][j],dp[i][j+1]),但要是dp[i+1][j+1]=dp[i][j]的话,就得-num[i][j],因为max(dp[i+1][j],dp[i][j+1])=dp[i][j],就会多计算一次
dp=[[0 for i in range(len(b))] for j in range(len(a))]
num=[[0 for i in range(len(b))] for j in range(len(a))]
for i in range(len(b)):
num[0][i]=1
for i in range(len(a)):
num[i][0]=1
for i in range(len(a)-1):
for j in range(len(b)-1):
if a[i]==b[j]:#显然a以a[i]结尾,b以b[j]结尾的最长公共子序列即dp[i+1][j+1]为上一状态加1即dp[i][j]+1
dp[i+1][j+1]=dp[i][j]+1
num[i+1][j+1]=num[i][j]
else:#若a[i]!=b[j],有两种状态会转移过来,a以a[i+1]结尾,b以b[j]结尾的最长公共子序列;a以a[i]结尾,b以b[j+1]结尾的最长公共子序列。取最大即可
dp[i+1][j+1]=max(dp[i][j+1],dp[i+1][j])
if dp[i+1][j+1]==dp[i][j]:
num[i+1][j+1]-=num[i][j]
if dp[i+1][j+1]==dp[i][j+1]:
num[i+1][j+1]+=num[i][j+1]
if dp[i+1][j+1]==dp[i+1][j]:
num[i+1][j+1]+=num[i+1][j]
ans1=dp[len(a)-1][len(b)-1]
ans2=num[len(a)-1][len(b)-1]
print(ans1)
print(int(ans2)%int(1e8))
最长公共子序列.2
a=input()
A=[]
str_a=""
b=input()
B=[]
str_b=""
for i in a:#将蓝肽切割为蓝肽子存储到列表
if 'A'<=i<='Z':
if str_a!="":
A.append(str_a)
str_a=""
str_a+=i
A.append(str_a)#将最后一个蓝肽子存储到序列
for i in b:
if 'A'<=i<='Z':
if str_b!="":
B.append(str_b)
str_b=""
str_b+=i
B.append(str_b)
dp=[[0 for i in range(len(B)+5)] for j in range(len(A)+5)]
for i in range(len(A)):#最长公共子序列模板dp[i][j]表示A序列以A[i]结尾,B序列以B[j]结尾的最长公共子序列的长度
for j in range(len(B)):
if A[i]==B[j]:
dp[i+1][j+1]=dp[i][j]+1
else:
dp[i+1][j+1]=max(dp[i][j+1],dp[i+1][j])
print(dp[len(A)][len(B)])
蓝桥骑士
n=int(input())
a=list(map(int,input().split()))
dp=[1 for i in range(len(a)+5)]#初始化为1,表示以自己结尾的递增子序列的长度为1
#dp[i]表示以a[i]结尾的递增子序列的长度,注意不是最长
ans=0
for i in range(len(a)):
for j in range(i):
if a[j]<a[i]:#遍历前i个元素,若小于a[i],那么dp[i]长度由dp[j]+1得来,取最大的那个
dp[i]=max(dp[i],dp[j]+1)
ans=max(ans,dp[i])
print(ans)
以上的动态规划会超时,下面是结合二分法的动态规划
import bisect
n=int(input())
a=[int(i) for i in input().split()]
b=[a[0]]
for i in range(1,n):
#第1个参数是列表,第2个参数是要查找的数,返回值为>=参数2的第一个索引(即二分法的>=x的最小值),列表中没有该元素,
#返回插入位置索引
index=bisect.bisect_left(b,a[i])
if index==len(b):
b.append(a[i])
else:
b[index]=a[i]
print(len(b))
下面给出一例子:
a=[10,9,2,5,3,7,101,18]
初始时b=[a[0]]=[10]
然后遍历a剩下的元素,去找比b中最后一个元素大的a[i],将它加到b中。a[i]比b的最后一个元素小怎么办呢?如题目所示,a[1]=9,此时index=bisect.bisect_left([10],9) =0,此时会更新b,b=[9]。找下一个a[2]=2,更新b=[2]。找下一个a[3]=5,此时index=bisect.bisect_left([2],5) =1,更新b=[2,5]。找下一个a[4]=3,此时index=bisect.bisect_left([2,5],3) =1,更新b=[2,3]。找下一个a[5]=7,更新b=[2,3,7]。找下一个a[6]=101,更新b=[2,3,7,101]。找下一个a[7]=18,更新b=[2,3,7,18]。
最后b的长度就是最长递增子序列
最优包含
s=list(input())
t=list(input())
dp=[[0 for i in range(len(t)+5)] for j in range(len(s)+5)]
#dp[i][j]表示s有i个字符,t有j个字符时所需要的最少操作次数
for i in range(1,len(t)+1):
dp[0][i]=99999#s没有字符,t有字符时,因为只能进行替换操作,因此这种情况操作无数次也不行
for i in range(len(s)):
for j in range(len(t)):
if s[i]==t[j]:
dp[i+1][j+1]=dp[i][j]
else:
dp[i+1][j+1]=min(dp[i][j]+1,dp[i][j+1])#要么把s[i]替换,此时操作+1;要么先不考虑s[i],那么此时的操作数为,s有i个字符[0-i),t有j+1个字符[0,j],即dp[i][j+1]的操作数
print(dp[len(s)][len(t)])
合唱队形
n=int(input())
t=list(map(int,input().split()))
dp1=[1 for i in range(n+5)]
dp2=[1 for i in range(n+5)]
for i in range(n):
for j in range(i):
if t[i]>t[j]:
dp1[i]=max(dp1[i],dp1[j]+1)
for i in range(n-1,0,-1):
for j in range(n-1,i,-1):
if t[i]>t[j]:
dp2[i]=max(dp2[i],dp2[j]+1)
ans=0
for i in range(n):
ans=max(ans,dp1[i]+dp2[i]-1)
print(n-ans)
路径
import math
def lcm(x,y):
return x*y/math.gcd(x,y)
dp=[float('inf') for i in range(2022)]#dp[i]表示1-i的距离,初始化为无穷大
dp[1]=0
for i in range(1,2021):
for j in range(i+1,i+22):
if j>2021:
break
dp[j]=min(dp[j],dp[i]+lcm(i,j))
print(int(dp[2021]))
day11
图论入门
1.数组存边
class edge:
def __init__(self,f,t,d):
self.f=f
self.t=t
self.d=d
a=[edge(0,0,0) for i in range(n)]
for i in range(n):
a[i].f,a[i].t,a[i].d=map(int,input().split())
2.最短路问题
弗洛伊德算法
for k in range(1,n+1):
for i in range(1,n+1):
for j in range(1,n+1):
dp[i][j]=min(dp[i][k]+dp[k][j],dp[i][j])
注:只能用于 n < 300 的小规模的图,它可以判断是否有负圈,dp[i][i]<0说明有负圈
弗洛伊德算法+二分法
import copy
n,p=map(int,input().split())
g=[[0]*(n+1) for i in range(n+1)]
m=[[0]*(n+1) for i in range(n+1)]
f=[[0]*(n+1) for i in range(n+1)]
for i in range(1,n+1):
l=list(map(int,input().split()))
for j in range(1,n+1):
g[i][j]=l[j-1]
for i in range(1,n+1):
l=list(map(int,input().split()))
for j in range(1,n+1):
m[i][j]=l[j-1]
f[i][j]=m[i][j]
def floyd(f):
a=0
for k in range(1,n+1):
for i in range(1,n+1):
for j in range(1,n+1):
f[i][j]=min(f[i][j],f[i][k]+f[k][j])
for i in range(1,n+1):
for j in range(1,n+1):
a+=f[i][j]
return a
def check(x,g):
f=copy.deepcopy(g)
h=x//n
s=x%n
for i in range(1,n+1):
for j in range(1,n+1):
if i==j:
continue
if i<=s:
f[i][j]=max(m[i][j],f[i][j]-h-1)
else:
f[i][j]=max(m[i][j],f[i][j]-h)
f[j][i]=f[i][j]#f[j][i]随着f[i][j]一起变化
return floyd(f)<=p
def slove():
global f,g,m
if floyd(f)>p:
print(-1)
return
else:
l=0
r=10000000
while l<r:
mid=(l+r)//2
if check(mid,g):
r=mid
else:
l=mid+1
print(r)
slove()
Dijkstra算法
import heapq#维护pq数组
INF=0x3f3f3f3f3f3f3f3f
n,m=map(int,input().split())
e = [[] for _ in range(m+1)]#数组存边
class Edge:#用于存储边
def __init__(self,f,to,w):
self.f=f#起点
self.to=to#终点
self.w=w#距离
for i in range(m):
u, v, w = map(int, input().split())
e[u].append(Edge(u, v, w))
class SNode:#用于存储中转结点的位置
def __init__(self,id,n_dis):
self.id=id
self.n_dis=n_dis#结点到起点的距离
def __lt__(self,other):
return self.n_dis<other.n_dis
pre=[-1]*(n+1)
def print_path(s,t):
global pre
if s==t:
print(s,end=" ")
return
print_path(s,pre[t])
print(t,end=" ")
def dijkstra():
global pre
s=1
done=[0]*(n+1)#记录结点是否被用于优化邻接点过
dis=[INF]*(n+1)#记录结点与起点的最短距离
dis[s]=0#起点与起点的最短距离显然为零
pq=[]#存储中转结点,按该点与起点的距离从小到大排序
heapq.heappush(pq,SNode(s,dis[s]))#压入起点
while pq:#中转结点用完程序结束
u=heapq.heappop(pq)#弹出距离起点最近的点
if done[u.id]:
continue#用过则找下一个点
done[u.id]=1#没用过就标记,接着用它优化邻接点
for y in e[u.id]:#通过该点找到所有邻接点所在边
if done[y.to]:#用过就找下一个点
continue
if dis[y.to]>y.w+u.n_dis:
dis[y.to]=y.w+u.n_dis#更新距离
heapq.heappush(pq,SNode(y.to,dis[y.to]))
pre[y.to]=u.id
for i in range(1,n+1):
if dis[i]>=INF:
print("-1",end=" ")
else:
print(dis[i],end=" ")
#打印路径
print()
for i in range(1,n+1):
print_path(s,i)
print()
dijkstra()
spfa算法
INF = 0x3f3f3f3f3f3f3f3f
n, m, s = map(int, input().split())
e = [[] for _ in range(m+1)]#数组存边
class Edge:
def __init__(self, to, w):#用于存储边
self.to = to#终点
self.w = w#距离
for i in range(m):
u, v, w = map(int, input().split())
e[u].append(Edge(v, w))
pre=[-1]*(n+1)
def print_path(s,t):
global pre
if s==t:
print(s,end=" ")
return
print_path(s,pre[t])
print(t,end=" ")
def spfa(s):
global pre
dist = [INF] * (n+1)#记录结点与起点的最短距离
inq = [0] * (n+1)#记录结点是否在队列
dist[s] = 0#起点与起点的最短距离显然为零
q = []#数组模拟队列
start=0#队头
end=0#队尾
q.append(s)#起点入队
end+=1#入队即加一个元素
inq[s] = 1#起点在队列中
while start!=end:#队列不为空
u = q[start]#取队头元素进行松弛
start+=1#删除队列的队头元素
inq[u] = 0#标记队头出队
if dist[u] == INF:#队头元素距起点无穷,显然不能用来松弛
continue#再取队头元素
for i in range(len(e[u])):#否则取该结点的所有邻接点
v = e[u][i].to
w = e[u][i].w
if dist[v] > dist[u] + w:#松弛所有邻接点
dist[v] = dist[u] + w
pre[v]=u
if inq[v] == 0:
q.append(v)#松弛后的邻接点入队
end+=1
inq[v] = 1
for i in range(1, n+1):
if dist[i] == INF:
print("-1", end=" ")
else:
print(dist[i], end=" ")
print()
for i in range(1,n+1):
print_path(s,i)
print()
spfa(s)
3.最小生成树
prim算法
inf=0x3f3f3f3f3f3f3f3f
demo=[]
m,n=map(int,input().split())#m个点,n条边
closest=[0]*m
lowcost=[0]*m
G=[[inf] * m for i in range(m)]
for i in range(n):
a,b,c=map(int,input().split())
G[a][b]=G[b][a]=c#邻接矩阵存储图
def prim():
global closest,lowcost,G,m
for i in range(m):
lowcost[i]=inf#候选最短边的权值,初始化为无穷大
closest[i]=0#候选点状态,值不为-1表示点在集合V中
closest[0]=-1#将起点放入集合U中
num=0
ans=0
e=0#表示最新加入集合U的点
while num<m-1:#最小生成树有顶点数减一条边,加入m-1条边
micost=inf#用于记录连接U,V集合的最短边
miedge=-1#用于记录连接U,V集合的最短边的V集合中的那个点
for i in range(m):
if closest[i]!=-1:#该点不在集合U中
temp=G[e][i]#在邻接矩阵中找到该点与最新点的距离
if temp<lowcost[i]:#如果距离比候选最短边小
lowcost[i]=temp#更新U-V集合中点的距离
closest[i]=e#可以存储此时最小生成树某条边的邻接点
if lowcost[i]<micost:#找到当前连接U,V的最短边的权值和,连接点
miedge=i
micost=lowcost[miedge]
ans+=micost#更新总权值
demo.append(micost)#存储权值
e=miedge#更新当前U集合最新点
closest[e] = -1
num+=1
return ans#最小生成树的总权值
print(prim())
for i in range(m-1):
print(demo[i],end=" ")
kruskal 算法
n,m=map(int,input().split())#n个点,m条边
father=[i for i in range(n+1)]#存储父节点
Q=[]#存储边信息
#cont=0
sum=0#最小生成树权值和
st=0#记录已经连接的边数
for i in range(m):
x,y,k=map(int,input().split())
Q.append({'x':x,'y':y,'k':k})
#cont+=k
Q.sort(key=lambda x:x['k'])#按边的权值从小到大排序
def find(x):#并查集查找根节点
if father[x]==x:
return x
father[x]=find(father[x])#记忆存储
return father[x]
for i in range(m):
tx=find(Q[i]['x'])#该边起点的父节点
ty=find(Q[i]['y'])#该边终点的父节点
if tx!=ty:#父节点不同,则属于两个连通分量,可以连接
sum+=Q[i]['k']#修改最小生成树权值和
st+=1
father[tx]=ty#合并两个连通分量
if st ==n-1:#最小生成树n-1条边
break
print(sum)
4.最近公共祖先
from collections import defaultdict
n, m = map(int, input().split())#n个电脑,m个问题
gra = defaultdict(list)#存储树结构
d = [0] * 100010#存储出度,即延时时间
for i in range(n - 1):
a, b = map(int, input().split())#a,b电脑相连
gra[a].append(b)#连接a的有[b,...]
gra[b].append(a)
d[a] += 1#出度+1
d[b] += 1
query = [[] for _ in range(100010)]#存储问题信息query[u]=[(v,i)]
res = [0] * 100010#存储答案
for i in range(m):
a, b = map(int, input().split())
if a != b:
query[a].append((b, i))
query[b].append((a, i))
else:
res[i] = d[a]
q = [0] * 100010#父节点
for i in range(1, n + 1):
q[i] = i
w = [0] * 100010#i点到起点的延时时间
st = [0] * 100010#tarjan算法标记数组
def find(x):
if x != q[x]:
q[x] = find(q[x])
return q[x]
def dfs(u, fa):
w[u] += d[u]#u结点到根节点的延迟时间
for g in gra[u]:#查找连接u的所有点
if g == fa:#排除父节点
continue
w[g] += w[u]#g结点到根节点的延迟时间
dfs(g, u)
def tarjan(u):
st[u] = 1#入u时标记
for j in gra[u]:#枚举u的孩子
if st[j] == 0:#孩子没走过
tarjan(j)#则进入孩子
q[j] = u#回u时孩子指向父亲
for item in query[u]:#离u时枚举以u为起点的问题的终点
y, id = item
if st[y] :#问题的终点走过即建树,那么可以找到公共祖先
anc = find(y)
#时间延迟为两点到起点时间延迟和减去最近公共祖先到
#起点的时间延迟的两倍最后加上最近公共祖先的出度
res[id] = w[y] + w[u] - w[anc] * 2 + d[anc]
dfs(1, -1)#深搜得到w数组
tarjan(1)#根节点开始
for i in range(m):
print(res[i])
day12
简单数论
1.取模运算
a**n mod b = (a mod b)**n mod b
2.快速幂
b,p,k=map(int,input().split())
def fast_pow(a,n,m):
res=1
a=a%m
while n>0:
if n&1==1:
res=res*a%m
a=a*a%m
n>>=1
return res
print(fast_pow(b,p,k))
3.最大公约数和最小公倍数
gcd(a,b)=gcd(|a|,|b|)
gcd(a,b)=gcd(a,a+b)=gcd(a,ka+b)
gcd(ka,kb)=k*gcd(a,b)
gcd(a,b,c)=gcd(gcd(a,b),c)
gcd(a,b)=d,则gcd(a/d,b/d)=1,即a/d与b/d互素
gcd(a+cb,b)=gcd(a,b)
lcm(a,b)=a/gcd(a,b)*b
def gcd(a,b):
if b==0:
return a
else:
return gcd(b,a%b)
def lcm(a,b):
return a/gcd(a,b)*b#先除防溢出
a,b,c=map(int,input().split())
k=lcm(a,b)
print(int(lcm(k, c)))
4.质数
n=int(input())
if n%2==1:
print(n*(n-1)*(n-2))
if n%2==0:
if n%3==0:
print((n-1)*(n-2)*(n-3))
else:
print(n*(n-1)*(n-3))
5.埃氏筛和欧式筛
n=int(input())
primes=[]
bprimes=[0]*(n+1)
bprimes[0]=1
bprimes[1]=1
cnt=0
def getprimes(n):
global primes,bprimes,cnt
for i in range(2,n+1):
if not bprimes[i]:
primes.append(i)
cnt+=1
#对于i*p,如果p<i,那么它一定被p标记过,可以进一步优化,从i*i开始枚举
for j in range(i*2,n+1,i):
bprimes[j]=1
getprimes(n)
if len(primes)!=0:
print(*primes)
print(cnt)
else:
print(cnt)
n=int(input())
primes=[]
bprimes=[0]*(n+1)
bprimes[0]=1
bprimes[1]=1
cnt=0
def getprimes(n):
global primes,bprimes,cnt
for i in range(2,n+1):
if not bprimes[i]:
primes.append(i)
cnt+=1
j=0
while j<cnt and i*primes[j]<=n:
bprimes[i*primes[j]]=1
if i%primes[j]==0:
break
j+=1
getprimes(n)
if len(primes)!=0:
print(*primes)
print(cnt)
else:
print(cnt)
6.分解质因子
p=[0]*30
c=[0]*30
def factor(n):
m=0
for i in range(2,int(n**0.5)+1):
if n%i==0:
m+=1
p[m]=i
c[m]=0
while n%i==0:
n=n//i
c[m]+=1
if n>1:
m+=1
p[m]=n
c[m]=1
return m
a,b=map(int,input().split())
for i in range(a,b+1):
m=factor(i)
print("{}=".format(i),end='')
for j in range(1,m+1):
for k in range(1,c[j]+1):
print(p[j],end='')
if k<c[j]:
print('*',end='')
if j<m:
print('*',end='')
print()
day13
组合数学
1.乘法原理
2.排列数
3.组合数
4.计数dp
import sys
dp=[0]*21
dp[0]=0
dp[1]=0
dp[2]=1
for i in range(3,21):
dp[i]=(i-1)*(dp[i-2]+dp[i-1])
for n in sys.stdin:#多行输入
n=int(n)
print(dp[n])
5.鸽巢原理
6.杨辉三角形
n=int(input())
def C(a,b):#求组合数
res=1
i=a
j=1
while j<=b:
res=int(res*i/j)
if res>n:
return int(res)#大于目标值提前结束
i-=1
j+=1
return int(res)
for k in range(16,-1,-1):#第33行16列为1166803110,大于1000000000
l=2*k#因此目标在16列之前
r=max(n,l)#如果目标值很小则看这列的第一个值
res=-1
while l<=r:#二分该列的行号
mid=(l+r)//2
if C(mid,k)>=n:
res=mid
r=mid-1
else:
l=mid+1
if C(res,k)==n:#找到则打印,否则找下一列
print((res+1)*res//2+k+1)
break