Blue Bridge Cup 20 日目 (Python) (クレイジーな質問の 3 日目)

質問の種類:

1. 思考問題・雑問題:数式、問題の意味分析、パターン発見

2. BFS/DFS: ワイド検索 (再帰的実装)、ディープ検索 (deque 実装)

3. 簡単な整数論:法、素数(int(sqrt(n))+1まで判定するだけ)、gcd、lcm、高速べき乗(ビット演算シフト演算)、大数分解(素数の積への分解)数字)

4. 単純なグラフ理論: 最短パス (1 対多 (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. 等差級数(gcd関数の使い方)

4. ファストパワー(Fast_pow、ビット演算、シフト演算)

5. 最大最小公倍数 (貪欲、列挙型の議論)

6. 素因数の分解(大きな数の分解、文字列処理関数)

7. ペーパーカッター(思考問題、内蔵機能の使用)

8. 蛇型の数字埋め(考え方・観察ルール)

9. 最大降雨量 (考える質問)

10. ソート(辞書順)

11. スマート モンキー (最小スパニング ツリー、チェック セット)

12. パス (floyd または dijstra の実装)

13. 出張(最短経路、マトリックスはダイジストラアルゴリズムを実装)

14. ブルーブリッジキングダム(ディジストラアルゴリズムテンプレート問題)

15. ブルーブリッジパーク (フロイドアルゴリズムテンプレートの質問)

ブラシの質問

1. 階乗近似(大数分解、循環)

import sys  #设置递归深度
import collections  #队列
import itertools  # 排列组合
import heapq  #小顶堆
import math
sys.setrecursionlimit(300000)


#写法一
save=[0]*101  # 大数分解
for i in range(1,101):
   for j in range(2,int(math.sqrt(i))+1):  # 质数从2开始
      while i%j==0:  # 质数分解
         save[j]+=1
         i=i//j
   if i>1:   # 剩下的数是一个质数或者本身就是一个质数 例如10=2*5  17=1*17
      save[i]+=1

ans=1
for i in save:
   ans*=(i+1)
print(ans)

# 写法二
##MAXN = 110  
##cnt = [0] * MAXN   #记录对应质数幂次
## 
##for i in range(1, 101):
##    x = i
##    # 质因子分解
##    j = 2
##    while j * j <= x:
##        if x % j == 0:  # 是一个质数约数
##            while x % j == 0:  #类似埃式筛
##                x //= j
##                cnt[j] += 1
##        j += 1
##    if x > 1:
##        cnt[x] += 1
## 
##ans = 1
##for i in range(1, 101):
##    if cnt[i] != 0:
##        ans *= (cnt[i] + 1)  # 0 也是一种选择
## 
##print(ans)
      

 100個なのでやり方は2つ!, そのため、大きな数を分解するには 1 ~ 100 をトラバースする必要があります。素数は 2 から始まることに注意してください。1 は素数ではありません。2 - int( sqrt(n) ) 区間をトラバースするだけで、 n の因数. Python は最後の 1 を取得できません したがって、 1 を足したからといってそれが素数であるというわけではなく、同時に、分解後に 1 より大きい場合は、分解されていないことを意味し、残りは10などの素数。素数でない場合は、4,9などの1になります。

2. 素因数の数(大きな数の分解、素数、近似数)

 

import sys  #设置递归深度
import collections  #队列
import itertools  # 排列组合
import heapq  #小顶堆
import math
sys.setrecursionlimit(300000)


#对一个数进行大数分解
ans=0
n=int(input())
for i in range(2,int(math.sqrt(n))+1):
   if n%i==0:  #发现质数
      ans+=1
      #print(i)  # 打印质数约数
      while n%i==0:  # 消除这个质数
         n=n//i
if n>1:
   #print(n)  # 打印质数约数
   ans+=1
print(ans)

サブ質問を送信します。最初の質問を満たした後、この質問は数秒で答えが得られます。この数を分解するだけです。素数がいくつあるかを尋ねるだけです。残りの数が大きいかどうかの判断に注意してください。分解して1より大きいということは、まだ残っていることを意味します。素数が見つかった場合、答えは1ずつ増加する必要があります。

3. 等差級数(gcd関数の使い方)

import sys  #设置递归深度
import collections  #队列
import itertools  # 排列组合
import heapq  #小顶堆
import math
sys.setrecursionlimit(300000)


# gcd(0,a)=a

n=int(input())
A=list(map(int,input().split()))
d=0
for i in range(len(A)-1):
   d=math.gcd(d,A[i+1]-A[i])
   #print(d) #打印d

# 需要处理d==0的情况
if d==0:
   print(n)
else:
   ans=(max(A)-min(A))//d+1
   print(ans)

サブ質問を送りますが、慎重に状況を判断する必要があります。以前、d==0の状況を見逃したことがあります。よく考えずに、÷0のエラーが発生しました。gcdに注意する必要があります( 0、a) = a長さが不確かな場合、境界範囲を決定する例を使用する場合。

4. ファストパワー(Fast_pow、ビット演算、シフト演算)

import sys  #设置递归深度
import collections  #队列
import itertools  # 排列组合
import heapq  #小顶堆
import math
sys.setrecursionlimit(300000)


b,p,q = map(int,input().split())
ans=1
while p:  # 8次方  转为二进制 1000
   if p&1: #当前位有1
      ans=ans%q * b
   b=b*b%q
   p=p>>1  # 右移即/2
print(ans%q)
   

サブ質問ですが、ビット演算をマスターする必要があり、左シフト2倍と右シフト2を削除します。while文を使用してループする場合、左足し右引き算など最後の値の変更に注意してください。無限ループを防ぐために右にシフトし、左にシフトします。

5. 最大最小公倍数 (貪欲、列挙型の議論)

import sys  #设置递归深度
import collections  #队列
import itertools  # 排列组合
import heapq  #小顶堆
import math
sys.setrecursionlimit(300000)


n=int(input())
# 第一时间想到 n*(n-1)*(n-2)
# 当n为奇数  最大值 n*(n-1)*(n-2)
# 当n为偶数  n和n-2可以约分
# n*(n-1)*(n-3)   (不能被3整除)
# (n-1)*(n-2)*(n-3) (能被3整除)

if n%2==1:
   print(n*(n-1)*(n-2))
else:
   if n%3==0:  #能被3整除
      print((n-1)*(n-2)*(n-3))
   else:
      print(n*(n-1)*(n-3))

 すべての場合を列挙するには、最初に貪欲なアイデアに従って 3 つの最大値を取得し、次に約数を持つ特殊な場合などの特殊な場合について議論し、互いに素な値を見つけます。最小公倍数の最大値を求める場合。

6. 素因数の分解(大きな数の分解、文字列処理関数)

import sys  #设置递归深度
import collections  #队列
import itertools  # 排列组合
import heapq  #小顶堆
import math
sys.setrecursionlimit(300000)



a,b=map(int,input().split())
for i in range(a,b+1):
   save=i
   ans=[]  # 保存分解的数
   for j in range(2,int(math.sqrt(i))+1):
      if i%j==0:
         while i%j==0:
            ans.append(str(j))
            i=i//j
   if i >1:  # 剩下的质数或者本身是质数没有分解,例如15,5,7
      ans.append(str(i))
   print(str(save)+'='+'*'.join(ans))  # " ".join(list)  list里面的元素需要为字符类型

      
            

 これらの数値をたどって、それぞれの数値を分解します。難しいのは、分解後の n 値 > 1 の場合、分解後に素数が残っているか、素数自体を他の素数に分解できないことを意味することを知ることです。文字列の結合操作、結合 () 関数は要素を文字列として接続する必要があります。

7. ペーパーカッター(思考問題、内蔵機能の使用)

 難しいことはありません。得点を与えることに属します。質問の意味を理解する必要があります。得点方法は 2 つだけです。組み込み関数 min() を使用して最小値を取得すると、得点が得られます。

8. 蛇型の数字埋め(考え方・観察ルール)

 探しているのは対角線上の要素です。対角線上の要素の法則、つまり +4 +8 +12 を観察してください。法則が見つかったら、それにループを付けるだけです。

9. 最大降雨量 (考える質問)

 34 ---------> 49-15=34、上のハンドはミスをしました

 質問を考え、重要なポイント、つまり 2 つの中央値に注意を払い、不明な場合は例を挙げ、結論を証明するために特別な例を使用します。

10. ソート(辞書順)

 辞書編集順、つまり 'a'>'b'、'a'>'ab'、'ab'>'b' の意味を理解します。この質問は辞書編集上の最短の順序を必要とするため、答えは固定されており、完全な配置、つまり N*(N-1)/2、つまり bca が abc の完全な配置 3*2 に配置されることを理解する必要があります。 /2=3

11. スマート モンキー (最小スパニング ツリー、チェック セット)

import sys  #设置递归深度
import collections  #队列
import itertools  # 排列组合
import heapq  #小顶堆
import math
sys.setrecursionlimit(300000)


def find(x):
   if x==f[x]:
      return f[x]
   else:
      f[x]=find(f[x])
      return f[x]
   
def merge(x,y):
   if find(x)!=find(y):  # 需要合根,默认合向y
      f[find(x)]=find(y)  # x的根指向y的根
         

m=int(input()) # 猴子树
leng=list(map(int,input().split()))  # 存储跳跃距离


n=int(input()) # 边数
dis = [0] # 存储坐标
for i in range(n):  
   dis.append(list(map(int,input().split())))
   
edge=[] #存储边
for i in range(1,n+1):
   for j in range(i+1,n+1):
      w=math.sqrt((dis[i][0]-dis[j][0])**2+(dis[i][1]-dis[j][1])**2) #计算距离
      edge.append((i,j,w))   # 添加边,总共添加n*(n-1)/2条边
edge.sort(key=lambda x:x[2])  # 边从小到大排序

Max=0
num=0  # 当前处理了多少条边
f=[ i for i in range(n+1)]
for i in edge:
   if find(i[0]) !=find(i[1]):   # 最小生成树算法处理
      merge(i[0],i[1])
      Max=max(Max,i[2])   #在遍历过程中记录下最长边
      num+=1

   if num==(n-1):  # 已经构建好了最小生成树
      break

ans=0  # 记录能跳的猴子数量
for i in leng:
   if i>=Max:
      ans+=1

print(ans)



最小スパニング ツリーの構築方法である和集合検索の使用に 精通しており、和集合検索を通じてクラスカル アルゴリズム アルゴリズムの使用方法を学習します。つまり、エッジを大きいものから小さいものにソートし、最短の辺をトラバースして最小スパニング ツリーを構築します。最小スパニングツリーを構築する方法。

12. パス (floyd または dijstra の実装)

import sys  #设置递归深度
import collections  #队列
import itertools  # 排列组合
import heapq  #小顶堆
import math
sys.setrecursionlimit(300000)


# 初始化边         
dp=[[2**100]*2030 for i in range(2030)]


def lcm(a,b):
   return a//math.gcd(a,b)*b
# 赋初值
for i in range(1,2022):
   for j in range(i+1,i+22):  # 取不到最后一位
      if j>2021:
         break
      dp[i][j]=lcm(i,j)

# floyd 算距离
for k in range(1,2022):
   for i in range(1,2):
      for j in range(1,2022):
         dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j])

print(dp[1][2021])
      

floyd アルゴリズムは多対多ですが、時間計算量は 3 次多項式計算量であり、ダイストラ計算量はそれより低く、1 対多であり、floyd アルゴリズムは 1 対多、多対多に変換できます。 、多対 1 。この質問の難しさは、floyd アルゴリズムを習得することにありますが、同時に、floyd は行列を通じて実装されることに注意する必要があります。 

import sys  #设置递归深度
import collections  #队列
import itertools  # 排列组合
import heapq  #小顶堆
import math
sys.setrecursionlimit(300000)


# Dijstr实现
def lcm(a,b):
   return a//math.gcd(a,b)*b
def dij(s):
   done=[0]*2022  # 标记是否处理过
   hp=[]
   dis[s]=0  # 自身到自身的距离为0
   heapq.heappush(hp,(0,s))  # 入堆
   while hp:
      u=heapq.heappop(hp)[1]  # 出堆元素结点,边最小
      if done[u]==0: # 没有被处理过
         done[u]=1
         #for i in dp[u]:  # 遍历u的邻居  i:(v,w)
         for i in range(len(dp[u])):  # 遍历u的邻居  i:(v,w)
            v,w=dp[u][i]
            if done[v]==0: # 没有被处理过
               dis[v]=min(dis[v],dis[u]+w)
               heapq.heappush(hp,(dis[v],v))
            
dp=[[] for i in range(2022)]  # 邻接表
dis=[2**100] * 2022  # 初始
# 邻接表更新
for i in range(1,2022):
   for j in range(i+1,i+22):
      if j>2021:
         break
      dp[i].append((j,lcm(i,j))) # 邻居和边长

s=1  # 开始起点
dij(s)
print(dis[2021])

      

13. 出張(最短経路、マトリックスはダイジストラアルゴリズムを実装)

 

import sys  #设置递归深度
import collections  #队列
import itertools  # 排列组合
import heapq  #小顶堆
import math
sys.setrecursionlimit(300000)



def dij():
   dist[1]=0  #很重要
   for _ in range(n-1): # 还有n-1个点没有遍历
      t=-1
      for j in range(1,n+1):
         if st[j]==0 and (t==-1 or dist[t]>dist[j]):  #找到没处理过得最小距离点
            t=j
      for j in range(1,n+1):
         dist[j]=min(dist[j],dist[t]+gra[t][j])  # t-j的距离,找最小值
      st[t]=1  # 标记处理过
   return dist[n]
      
n,m=map(int,input().split())
 #下标全部转为从1开始
stay=[0]+list(map(int,input().split()))
stay[n]=0   
gra = [[float('inf')] * (n+1) for _ in range(n+1)]
dist = [float('inf')] * (n+1)
st=[0]*(n+1)  # 标志是否处理


for i in range(m):
   u,v,w=map(int,input().split()) #这里重构图
   gra[u][v]=stay[v]+w
   gra[v][u]=stay[u]+w


print(dij())
   

この質問の難しさは、地図を再構築すること、つまり、各都市で費やした時間を地図に更新することです。同時に、DIjstra アルゴリズムも非常に重要です。

テンプレート: (隣接リスト)

heapq を通じて実現される隣接接続テーブルは (v, w) を格納し、小さな上部ヒープを使用して格納し、ヒープが解放されるたびに最小の初期距離を持つ頂点を取得し、その隣接ノードが処理されたかどうかを判断します。距離でない場合は隣接ポイントを更新し、計算された距離をパイルに入れます。マーカー行列、距離行列、および隣接リストが存在する必要があります。

テンプレート: (行列)

エッジ情報は行列に保存され、残りの点は n-1 サイクルで処理され、初期点から最も短い未処理の点が検索され、他の点の距離値が初期点から更新されます。それをポイントして最小値を見つけます。ラベル行列、距離行列、およびエッジを保存する行列が必要です。

 ベルマンフォードアルゴリズム

n,m=map(int,input().split())
t=[0]+list(map(int,input().split()))
e=[]  #简单的数组存边

for i in range(1,m+1):
    a,b,c = map(int,input().split())
    e.append([a,b,c])  # 双向边
    e.append([b,a,c])

dist=[2**50]*(n+1)
dist[1]=0

for k in range(1,n+1): # 遍历每个点,n个点,执行n轮问路
    for a,b,c in e:  # 检查每条边,每一轮问路,检查所有边
        res=t[b]
        if b==n:
            res=0
        dist[b]=min(dist[b],dist[a]+c+res)  # 更新路径长度

print(dist[n])

14. ブルーブリッジキングダム(ディジストラアルゴリズムテンプレート問題)

 

import heapq  # 导入堆
def dij(s):
    done=[0 for i in range(n+1)]  # 记录是否处理过
    hp=[]  #堆
    dis[s]=0
    heapq.heappush(hp,(0,s))   #入堆,小顶堆
    while hp:
        u=heapq.heappop(hp)[1] #出堆元素结点
        if done[u]: #当前结点处理过
            continue
        done[u]=1
        for i in range(len(G[u])): #遍历当前结点的邻居
            v,w =G[u][i]
            if done[v]:continue
            dis[v]=min(dis[v],dis[u]+w)  # 更新当前结点邻居的最短路径
            heapq.heappush(hp,(dis[v],v))
 
 
 
 
n,m = map(int,input().split())#
s=1  # 从1开始访问
G=[[]for i in range(n+1)]   #邻接表存储
inf = 2**50
dis = [inf]*(n+1) #存储距离
for i in range(m):# 存边,这里是单向边
    u,v,w = map(int,input().split())
    G[u].append((v,w))  #记录结点u的邻居和边长
 
dij(s)
for i in range(1,n+1):
    if dis[i]==inf:
        print("-1",end=' ')
    else:
        print(dis[i],end=' ')

テンプレートの質問はマスターして覚えておく必要があります。これは heapq スモールトップ ヒープによって実現されます。テンプレートをマスターするだけで十分です。

15. ブルーブリッジパーク (フロイドアルゴリズムテンプレートの質問)

import os
import sys
 
# 请在此输入您的代码
#floyd算法,多对多
 
 
def floyd():
  global dp
  for i in range(1,n+1):
    for j in range(1,n+1):
      for k in range(1,n+1):
        dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j])
 
n,m,q = map(int,input().split())
inf=2**120
dp=[[inf]*(n+1) for i in range(n+1)]
choice=[]
for i in range(m):
  u,v,w=map(int,input().split())
  dp[u][v]=w
  dp[v][u]=w
for i in range(q):
  s,d = map(int,input().split())
  choice.append((s,d))
floyd()
for s,d in choice:
  if dp[s][d]!=inf:
    print(dp[s][d])
    continue
  print(-1)
   

フロイド アルゴリズム テンプレートの質問、よくマスターしてください。フロイドは多対多で使用されます。

おすすめ

転載: blog.csdn.net/weixin_52261094/article/details/129899614