2021年软件类第十二届蓝桥杯 省赛 python组 F-J题解
2021年軟件類第十二屆藍橋杯 省賽 python組 F-J題解
文章目錄
- 2021年軟件類第十二屆藍橋杯 省賽 python組 F-J題解
 - 試題 F:時間顯示
 - 題目描述
 - 輸入描述
 - 輸出描述
 - 輸入輸出樣例
 - 示例 1
 - 示例 2
 
- 評測用例規模與約定
 - 思路
 - 代碼1
 - 代碼2(利用datetime庫)
 
- 試題 G:楊輝三角形
 - 題目描述
 - 輸入描述
 - 輸出描述
 - 輸入輸出樣例
 - 示例 1
 
- 評測用例規模與約定
 - 思路
 - 代碼
 
- 試題 H:左孩子右兄弟
 - 題目描述
 - 輸入描述
 - 輸出描述
 - 輸入輸出樣例
 - 示例 1
 
- 評測用例規模與約定
 - 思路
 - 代碼
 
- 試題 I:異或數列
 - 題目描述
 - 輸入描述
 - 輸出描述
 - 輸入輸出樣例
 - 示例 1
 
- 評測用例規模與約定
 - 思路
 - 代碼
 
- 試題 J:括號序列
 - 題目描述
 - 輸入描述
 - 輸出描述
 - 輸入輸出樣例
 - 示例 1
 
- 評測用例規模與約定
 - 思路
 - 代碼
 
備戰藍橋杯的時候,記錄一下寫的題目和一些思考和理解
這里面的題目都可以從https://www.lanqiao.cn/courses/2786/learning/?id=280833得到,寫了一些python的解法
 在這一部分之中,我也配套做了一下視頻的講解,如果看題解太干澀,也可以去b站看看視頻,這里給出鏈接B站視頻
試題 F:時間顯示
題目描述
小藍要和朋友合作開發一個時間顯示的網站。
在服務器上,朋友已經獲取了當前的時間,用一個整數表示,值為從 1970 年 1 月 1 日 00:00:00 到當前時刻經過的毫秒數。
現在,小藍要在客戶端顯示出這個時間。小藍不用顯示出年月日,只需要顯示出時分秒即可,毫秒也不用顯示,直接舍去即可。
給定一個用整數表示的時間,請將這個時間對應的時分秒輸出。
輸入描述
輸入一行包含一個整數,表示時間。
輸出描述
輸出時分秒表示的當前時間,格式形如 HH:MM:SS,其中 HH 表示時,值為 0 到 23,MM 表示分,值為 0 到 59,SS 表示秒,值為 0 到 59。時、分、秒 不足兩位時補前導 0。
輸入輸出樣例
示例 1
輸入
46800999輸出
13:00:00示例 2
輸入
1618708103123輸出
01:08:23評測用例規模與約定
對于所有評測用例,給定的時間為不超過 101810^{18}1018 的正整數。
思路
對于這道題來說,其實也很簡單,不過我這里給兩種做法
第一種就是計算時間,這就需要我們計算秒,比如一天有24x60x60ms,然后計算天,時,分,秒,然后就可以得出來
第二種就是利用datetime庫,那真的是很簡單
代碼1
# https://www.lanqiao.cn/problems/1452/learning/ t = int(input()) t = t//1000 # 轉化為秒s d = t % (24*60*60) # 得到一天的秒數,因為一天有24*60*60s hh = d//(3600) # 得到小時,因為一小時有3600 mm = d%3600//60 ss = d%60 print("%02d:%02d:%02d" % (hh, mm, ss))代碼2(利用datetime庫)
# https://www.lanqiao.cn/problems/1452/learning/ import datetime date = int(input())start = datetime.datetime(1970,1,1,00,00,00) timedelta = datetime.timedelta(milliseconds=date) now = start+timedelta strf = now.strftime("%H:%M:%S") print(strf)試題 G:楊輝三角形
題目描述
下面的圖形是著名的楊輝三角形:
如果我們按從上到下、從左到右的順序把所有數排成一列,可以得到如下數列:$ 1, 1, 1, 1, 2, 1, 1, 3, 3, 1, 1, 4, 6, 4, 1, \cdots$
給定一個正整數 N,請你輸出數列中第一次出現 N 是在第幾個數?
輸入描述
輸入一個整數 N。
輸出描述
輸出一個整數代表答案。
輸入輸出樣例
示例 1
輸入
6輸出
13評測用例規模與約定
對于 20% 的評測用例,1≤N≤101\leq N\leq 101≤N≤10; 對于所有評測用例,1≤N≤10000000001\leq N\leq 10000000001≤N≤1000000000。
思路
實際上呢,這一道題是一道思維題,一開始我也搞不清楚這一道題要什么,后面發現居然是一道規律題。
這道題實在是有些復雜,首先我們就可以用我們的組合數,這個組合數我們也可以直接通過math庫里面進行返回,實在不會我們也可以利用數學公式進行求得,也就是一個循環就可以得到。這里說明一下,好像在3.8之前的版本,這個函數是放在itertools里面的,不過現在調用math庫即可,因為我們的官方環境是3.8.6
好吧,接下來就介紹一下這道題,我覺得吧,楊輝三角形最好的便是尋找規律,我們可以看到下圖,是我做的一個筆記,我們需要看這個斜邊,我們可以發現這個斜邊實際上是有規律的,由于兩邊是堆成的,所以我們只需要看一邊
我們會發現以上的規律,首先是單調遞增的,其次,如果我們得到邊的序號x,那我們的L=2x,比如我上面舉的例子,第一條便就是2行,第二條邊就是4行,第三條邊就是6行,所以所有邊的第開始的第一個序號就是我們的C2xxC_{2x}^{x}C2xx?。
為了找到我們的數,我們就需要二分找到我們的[L,R]的區間,這里要注意的是,斜邊上的數,都是出現的第一個數,換句話來說,就是第一次出現的數。
接著我們就需要確立第幾個數了,首先我我們可以設 n=Cqxn = C_q^xn=Cqx?
我們依舊可以找規律
C42=6?>(1+2+3+4)+2+1=13C_4^2=6 -> (1+2+3+4) + 2 + 1 = 13C42?=6?>(1+2+3+4)+2+1=13
C52=10?>(1+2+3+4+5)+2+1=18C_5^2 = 10 -> (1+2+3+4+5) + 2+1=18C52?=10?>(1+2+3+4+5)+2+1=18
C71=7?>(1+2+3+4+5+6+7)+1+1=30C_7^1=7->(1+2+3+4+5+6+7)+1+1=30C71?=7?>(1+2+3+4+5+6+7)+1+1=30
所以總結就是,如果n=Cqxn=C_q^xn=Cqx?,那就就在(1+2+3+...+q)+x+1=(q+1)?q/2+x+1(1+2+3+...+q)+x+1=(q+1)*q/2 + x + 1(1+2+3+...+q)+x+1=(q+1)?q/2+x+1個位置
然后我們所說的二分查找就是在不同的斜邊來說,查找數,比如說第x條斜邊中,我們查找q
代碼
# https://www.lanqiao.cn/problems/1457/learning/ n = int(input()) import math # 求組合數 def C(a, b):# return math.comb(a,b) # 可以調用數學庫,直接計算res = 1i = aj = 1while j <= b:res = res * i // j # 分子是b的階乘,b!if res > n: # 組合數是遞增的,這時候已經比他大了,直接返回便可return resi -= 1j += 1return res# 二分查找n,由于數字在1000w之內,所以確立一個上界為16 for k in range(16, -1, -1):l = 2 * k # 每一個斜邊的上的數 L = 2*xr = max(n, l) res = -1# 二分法尋找while l <= r:mid = l + r >> 1if C(mid, k) >= n: # 如果C(mid,k) >= nres = mid r = mid - 1else:l = mid + 1if C(res, k) == n: # 這時候已經找到我們的行數res,和在第k個print((res + 1) * res // 2 + k + 1)break試題 H:左孩子右兄弟
題目描述
對于一棵多叉樹,我們可以通過 “左孩子右兄弟” 表示法,將其轉化成一棵二叉樹。
如果我們認為每個結點的子結點是無序的,那么得到的二叉樹可能不唯一。
換句話說,每個結點可以選任意子結點作為左孩子,并按任意順序連接右兄弟。
給定一棵包含 NN 個結點的多叉樹,結點從 1 至 N編號,其中 1 號結點是根,每個結點的父結點的編號比自己的編號小。
請你計算其通過 “左孩子右兄弟” 表示法轉化成的二叉樹,高度最高是多少。
注:只有根結點這一個結點的樹高度為 0。
輸入描述
輸入的第一行包含一個整數 N。 以下 N ?1 行,每行包含一個整數,依次表示 2 至 N 號結點的父結點編號。
輸出描述
輸出一個整數表示答案。
輸入輸出樣例
示例 1
輸入
5 1 1 1 2輸出
4評測用例規模與約定
對于 30%30\%30%的評測用例,1≤N≤201 \leq N \leq 201≤N≤20;
對于所有評測用例,1≤N≤1000001 \leq N \leq 1000001≤N≤100000。
思路
對于這道題來說,左孩子右兄弟,我們需要達到最深的二叉樹,比較簡單的來說,就是每次選擇孩子更多的作為最后一個結點
比如對于這樣一幅圖,我們可以有以下的方法,我列出了其中的三種方法
我們可以看到,最深的就是最后一個,唯一的卻別就是2有一個孩子5,所以深度就增加了
好了,簡單理清思路我們就可以用我們的dfs了,首先我們可以從輸入中創建鄰接表,可以判斷兩個結點是否有聯系。
每次的最大深度就是我們孩子數量,然后又加上孩子的孩子的數量,我們就用了深度優先搜索DFS,每次DFS孩子,取最大深度,最后從根,也就是1開始DFS,最后輸出dp[1]就是我們的結果。
dp[u]:以點 u 為根節點,通過 “左孩子右兄弟” 表示法轉化成二叉樹后的最大高度;
dp[u] = 子節點數量 + 子樹轉化為二叉樹后的最大高度
代碼
# https://www.lanqiao.cn/problems/1451/learning/ import sys # 樹形DP g = [[] for j in range(1, 100100)] # 鄰接表 dp = [0] * 100100# 設置遞歸深度 sys.setrecursionlimit(150000)def dfs(u):dp[u] = len(g[u]) # 全部變為左孩子,那么長度就是孩子的數量maxv = 0for v in g[u]:dfs(v) # DFS孩子maxv = max(dp[v], maxv) # 取最大深度dp[u] += maxv # 每次加上最大深度n = int(input()) for i in range(2, n + 1):v = int(input())g[v].append(i) dfs(1) print(dp[1])試題 I:異或數列
題目描述
Alice 和 Bob 正在玩一個異或數列的游戲。初始時,Alice 和 Bob 分別有一個整數 a 和 b,初始值均為 0。
有一個給定的長度為 n 的公共數列 X1,X2,?,XnX_1, X_2,\cdots , XnX1?,X2?,?,Xn。Alice 和 Bob 輪流操作,Alice 先手,每步可以在以下兩種選項中選一種:
選項 1:從數列中選一個 XiX_iXi? 給 Alice 的數異或上,或者說令 a 變為 a⊕Xia \oplus X_ia⊕Xi?。(其中 ⊕\oplus⊕表示按位異或)
選項 2:從數列中選一個 XiX_iXi? 給 Bob 的數異或上,或者說令 b 變為 b⊕Xib \oplus X_ib⊕Xi?。
每個數 XiX_iXi? 都只能用一次,當所有 XiX_iXi? 均被使用后(n 輪后)游戲結束。游戲結束時,擁有的數比較大的一方獲勝,如果雙方數值相同,即為平手。 現在雙方都足夠聰明,都采用最優策略,請問誰能獲勝?
輸入描述
每個評測用例包含多組詢問。詢問之間彼此獨立。
輸入的第一行包含一個整數 T,表示詢問數。
接下來 T 行每行包含一組詢問。其中第 i 行的第一個整數 nin_ini? 表示數列長度,隨后 nin_ini? 個整數 X1,X2,?,XniX_1, X_2, \cdots , X_{n_i}X1?,X2?,?,Xni??表示數列中的每個數。
輸出描述
輸出 T行,依次對應每組詢問的答案。 每行包含一個整數 1、0 或?1 分別表示 Alice 勝、平局或敗。
輸入輸出樣例
示例 1
輸入
4 1 1 1 0 2 2 1 7 992438 1006399 781139 985280 4729 872779 563580輸出
1 0 1 1評測用例規模與約定
對于所有評測用例,1≤T≤2000001\leq T\leq 2000001≤T≤200000,1≤∑i=1Tni≤2000001 \leq \sum\limits_{i=1}^T n_i \leq 2000001≤i=1∑T?ni?≤200000,0≤Xi<2020,0\leq X_i < 20^{20},0≤Xi?<2020。
思路
這道題想了一會,首先來說對于每一個人,我們都有兩種選擇,既然足夠聰明,就有兩種想法,第一是讓自己的數更大,第二就是讓別人的數更小,所以對于先手來說,由于兩個人都是0,所以會選擇較大的數對自己異或
這里要記住兩個異或的重要公式:a^a = 0, a ^ 0 = a
那我們判斷一下,其實我們可以發現 a^b = X1^ X2 ^ X3… Xn,若a^b = 0,也就是說明我們的a=b,也就是平局的情況,所以會對所有數進行異或,如果sum=0,就說明是平局
其次就是sum!=0的情況,對于這種情況來說,我們就需要看最高位了,比如100的最高位是第3位為1,我們就要看最高位為one的個數了
例如我們有A,B,C三個最高位都為1的數,那我們對于我們先手來說,就會先把最大數的和自己異或,保證自己的數最大
之后可能1.后手用B與先手的a異或,使其的最高位為0,然后先手再拿C與自己的數異或,最高位還是1。2.或者是后手用B與自己數異或,使自己的數最高位為1,然后先手就會用C使其后手的數異或,讓他變為1。我們會發現,這時候先手是一定贏的,因為他的最高位是1。
但是假設,我們還有最高位為0的數,如果多一個數,那么我們的后手就會把先手的最高位變成0,那么后手就贏了,如果有兩個,那么先手就可以與自己的數異或,使自己的數的最高位還是1。
所以就可以總結一下
1.第一種情況:sum=0,說明Alice和Bob一定平手
2.第二種情況:sum!=0
計算出最大數的最大數位,假設是第i位,設這些數中有one個數在該為1,zero個數在該位為0
a.如果one是偶數,說明判斷不出來,繼續枚舉下一位。
b.如果one是奇數,one只有一個,一定是Alice贏
one>1,zero是偶數,一定是Alice贏
one>1,zero是奇數,一定是Bob贏
代碼
# https://www.lanqiao.cn/problems/1450/learning/T = int(input()) for i in range(T):a,b = 0,0arr = list(map(int,input().split()))sum = 0ma = 0for x in arr[1:]:sum ^= xma = max(ma,x) # 找出最大位的數if sum == 0: # a^b = X1^X2^X3...Xn=0print(0) # 平局continuex = 1while x < ma:x <<= 1 # 最高位if x > ma:x >>= 1while x > 0:one = 0zero = 0for i in arr[1:]:if i&x == 0: # x[i]的第i位是否為0zero += 1else:one += 1if one%2 == 1: # one為奇數if zero%2 == 1 and one > 1: # zero為奇數print(-1)else: # one 為1或者是zero為偶數print(1)breakx >>= 1 # 對于x的第i位的one為偶數,則判斷下一位試題 J:括號序列
題目描述
給定一個括號序列,要求盡可能少地添加若干括號使得括號序列變得合法,當添加完成后,會產生不同的添加結果,請問有多少種本質不同的添加結果。
兩個結果是本質不同的是指存在某個位置一個結果是左括號,而另一個是右括號。
例如,對于括號序列 (((),只需要添加兩個括號就能讓其合法,有以下幾種不同的添加結果:()()()、()(())、(())()、(()()) 和 ((()))。
輸入描述
輸入一行包含一個字符串 s,表示給定的括號序列,序列中只有左括號和右括號。
輸出描述
輸出一個整數表示答案,答案可能很大,請輸出答案除以 1000000007 (即 109+710^9 + 7109+7) 的余數。
輸入輸出樣例
示例 1
輸入
((()輸出
5評測用例規模與約定
對于 40% 的評測用例,∣s∣≤200|s| \leq 200∣s∣≤200。
對于所有評測用例,1≤∣s∣≤50001 \leq |s| \leq 50001≤∣s∣≤5000。
思路
這道題是最近做的,做的時候可苦死我們了,是真的難,到后面也只拿了50分,不過我是真卷不懂了
這道題首先要明白題目的一個定義,什么叫合法的括號序列
合法序列的定義:對于一段括號序列,從左往右起,一個字符一個字符的往前看,對于每一段小的括號序列“(” 數量 大于等于 “)” 數量,那么整個括號序列就合法。
所以我們要注意,我們可以左括號數大于我們的右括號數的,但是我們的右括號數不能大于我們的左括號數
明白了定義后,我們就看一下題目,首先我們需要添加我們的左括號和由括號,那我們的添加是不是相互獨立的呢,答案當然是肯定的考慮到我們只能在空隙中插入括號, 如果我們添加的一對左右括號不是在同一個空隙中, 那么他們顯然是互不干擾的;如果是添加在同一個空隙中, 那么他們的添加順序是唯一的, 只能是)(, 因為如果是()的話, 那我們本次的添加就是無效的, 不滿足添加最少的括號使得序列得到匹配。 由此可得, 我們只需要單獨計算出添加左括號的方案數, 乘上單獨添加右括號的方案數就是答案的數量。
明確了這一點之后,我們就可以分開討論,我們可以判斷在原括號序列添加左括號使之變得合法,然后變換原括號序列(先將括號序列逆序,再將左括號變成右括號,右括號變成左括號),這樣調整之后我們在變換后的括號序列中添加左括號使之變得合法(相當于在原括號序列添加右括號)。
然后接著就是這道題的核心部分,這里面就定義了一個DP數組,dp[i][j][i ][j ][i][j]表示當前枚舉到第i個右括號, 我們添加了j個左括號的合法方案,這里面會有一種情況,根據定義,就是如果我們添加的操作使得前i個右括號都被匹配完后還有剩余的左括號, 我們仍認為這個狀態是合法的。也就是說,我們是不在意括號加多少個,我們在意的是左括號比右括號多多少個,并且j應該是一定大于0,因為我們枚舉到第i個右括號,我們是一定存在右括號的,不可能是0種。
我們如果以右括號為端點, 將整個序列進行分割, 那么在分割后的每一小段添加左括號的方案數顯然只和這段序列中左括號的數量有關, 因為這段序列里全是左括號, 怎么排列都是一種。所以我們只關注左括號的個數就好了, 更準確的來說, 我們只要關注我們添加的左括號的個數。
遇到左括號的時候,證明在這個左括號之前的括號序列已經被判斷過如何使其合法了,那么加上這個左括號依然合法,所以我們不需要管這個左括號。也就是在他前面的括號序列添加左括號使其合法的種數等于加上這個左括號之后這個序列需要添加的左括號種數。dp[i][j]=dp[i?1][j?1]dp[i][j]=dp[i-1][j-1]dp[i][j]=dp[i?1][j?1]
如果遇到我們的右括號,如果這個加上這個右括號的序列本身合法,那么我們僅需添加0個左括號就能使其合法,如果不合法就需要添加剛好使得其合法的左括號甚至可以更多。
 dp[i][j]=dp[i?1][0]+dp[i?1][1]+…+dp[i?1][j]+dp[i?1][j+1]dp[i][j] = dp[i-1][0] + dp[i-1][1] + … + dp[i-1][j] + dp[i-1][j+1] dp[i][j]=dp[i?1][0]+dp[i?1][1]+…+dp[i?1][j]+dp[i?1][j+1]
 簡單解釋一下要得到前i個字符的序列左括號比右括號多j個的合法情況,
前i-1個字符的序列左括號比右括號多0個(剛好合法)的情況,再在這個右括號前面加j+1個左括號得到
 前i-1個字符的序列左括號比右括號多1個的情況,再在這個右括號前面加j個左括號得到
…
 前i-1個字符的序列左括號比右括號多j+1個的情況,再在這個右括號前面加0個左括號得到
可是這樣會超時,所以這里就把三維壓縮成二維,我們會發現,dp[i][j?1]=dp[i?1][0]+dp[i?1][1]+…dp[i?1][j]dp[i][j-1] = dp[i-1][0] + dp[i-1][1] + … dp[i-1][j]dp[i][j?1]=dp[i?1][0]+dp[i?1][1]+…dp[i?1][j],那么這樣的話,我們就可以得到
 dp[i][j]=dp[i?1][j+1]+dp[i][j?1]dp[i][j] = dp[i-1][j+1] + dp[i][j-1] dp[i][j]=dp[i?1][j+1]+dp[i][j?1]
代碼
# https://www.lanqiao.cn/problems/1456/learning/#括號序列 MOD = (int)(1e9 + 7)def works():dp = [[0]*5000 for _ in range(5000)]dp[0][0] = 1 # 初始化for i in range(1, n + 1): # 一個括號一個括號進行判斷if str[i] == '(': # 遇到左括號for j in range(1, n + 1):dp[i][j] = dp[i - 1][j - 1]else:dp[i][0] = add(dp[i - 1][0], dp[i - 1][1])# dp[i][j] = dp[i - 1][0] + dp[i - 1][1] + ...... + dp[i - 1][j]# dp[i][j + 1] = dp[i - 1][0] + dp[i - 1][1] + ...... + dp[i - 1][j] + dp[i - 1][j + 1]for j in range(1, n + 1):# 最多加n個括號dp[i][j] = add(dp[i - 1][j + 1], dp[i][j - 1])for i in range(n + 1):if dp[n][i]:return dp[n][i]# 我們需要的就是長度為len添加括號的合法情況,而從前往后遍歷出現的第一個有可能的情況就是需要括號數最少的情況,因為左括號可以加很多個,我們僅需添加最少的情況def add(x, y): return (x + y) % MOD str = list(input()) n = len(str)str.insert(0, 0) #使目標字符串下標從1開始 ans_l = works()str.reverse() for i in range(n):if str[i] == '(':str[i] = ')'else:str[i] = '(' str.insert(0, 0) #使目標字符串下標從 1 開始 ans_r = works()print(ans_l * ans_r % MOD)總結
以上是生活随笔為你收集整理的2021年软件类第十二届蓝桥杯 省赛 python组 F-J题解的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: 人脸识别技术简介
 - 下一篇: 丹佛斯变频器al13故障_丹佛斯变频器维