一、2023.
1. 考点:枚举
2. 难度:⭐⭐⭐
3. 要点分析
利用列表的pop(),来检验顺序的正确性,是一个很好的思路。
count = 0
for i in range(12345678, 98765432+1):
x = [t for t in str(i)]
if '2' not in x and '0' not in x and '3' not in x:
continue
pos = ['2', '0', '2', '3']
for t in x:
if len(pos)>0 and pos[0]==t:
pos.pop(0)
if len(pos)==0:
count += 1
print(98765433-12345678-count)
二、硬币兑换。
1. 考点:枚举
2. 难度:⭐⭐
3. 要点分析:
①读懂题目(一开始我就没看懂...)。
②枚举的时候,注意是否有重复计算。
import sys
# 兑换出来的硬币最大为 4046
result = [0]*(2023*2+1)
for i in range(2, 2023+1):
for j in range(1, 2023+1):
if i==j: #币值相同
result[i+j] += i//2
else: #币值不同
result[i+j] += min(i,j)
print(max(result)//2) #2+3和3+2属于同种情况,循环里重复计算了
三、松散子序列。
1. 考点:动态规划
2. 难度:⭐⭐⭐⭐
3. 要点分析
(1)状态表示。
表示到序列第i个位置时的最优值。
(2)转移关系(选择当前元素、不选择当前元素)。
前半部分表示当前不选该位置元素,后半部分表示当前选择该位置元素。
(3)初始状态:
由于相邻之间的元素不能取,因此需要获得前两个位置的最佳选择。
基于转移关系,可推导:
(4)边界条件。
如果序列中只有一个元素,则子序列的值为该元素的值。
def dps(s):
# 记录数组
dp = [0] * len(s)
# 初始条件1
dp[0] = ord(s[0]) - 96
# 边界条件
if len(s) == 1:
return dp[-1]
# 初始条件2
dp[1] = max(ord(s[0]) - 96, ord(s[1]) - 96)
# 状态转移
for i in range(2, len(s)):
dp[i] = max(dp[i - 1], dp[i - 2] + (ord(s[i])) - 96)
return dp[-1]
s = input()
print(dps(s))
四、管道。
1. 考点:二分、贪心
2. 难度:⭐⭐⭐
3. 要点分析:
①每一次开闸时的水流覆盖区域,可以看作是一条“线段”,这样便将问题转化为“区间覆盖”问题。
②“二分法”的应用场景在于,存在一个“临界值”可以将整体分成两个部分,一部分满足条件,另一部分不满足;利用“二分法”找到满足条件的时间,大大地减少了计算量。
③如果二分得到的时间点 mid 符合条件,因为大于 mid 的时间点也一定符合条件,所以更新 right=mid,否则更新 left=mid+1;重复这个过程,直到搜索范围的左右端点相等,此时就找到了最早的时间。
import sys
n, length = map(int, input().split())
l = [0]*n
s = [0]*n
for i in range(n):
l[i], s[i] = map(int, input().split())
def check(t):
cover = []
for i in range(n):
cover.append([max(1,l[i]-t+s[i]), min(l[i]+t-s[i],length)])
cover = sorted(cover)
if cover[0][0]!=1:
return False
point = cover[0][1] #表示已经覆盖到的区域
for i in range(1, n):
if cover[i][0]-point > 1: #左端点与右端点的差值不超过1
break
else:
point = max(point, cover[i][1]) #更新覆盖区域
return point>=length
left, right = 1, 10**10
t = 0
while left < right:
mid = (left+right)//2
if check(mid):
# print(mid)
right = mid
else:
left = mid+1
print(right)
五、保险箱。
1. 考点:动态规划
2. 难度:⭐⭐⭐⭐⭐
3. 要点分析
(1)状态表示。
表示第i个位置的第j种方法。
(2)转移关系(退位、不进不退、退位)。
(3)初始状态。
——不进不退
——往前转&进位
——往后转&退位
如:3 --> 1
不进不退:3,2,1(2个)
进位:3,4,5,6,7,8,9,0,1(8个)
退位:3,2,1,0,9,8,7,6,5,4,3,2,1(12个)
import sys
n = int(input())
s1 = input()[::-1]
s2 = input()[::-1]
#字符串反转后第一位是个位,第二位是十位...
s1 = [int(t) for t in s1]
s2 = [int(t) for t in s2]
dp = [[0]*3 for _ in range(n+1)]
dp[0][0] = abs(s1[0]-s2[0]) #不进退直接加减
dp[0][1] = 10-s1[0]+s2[0] #进位
dp[0][2] = 10+s1[0]-s2[0] #退位
for i in range(1,n):
"""
上一位不进退直接加减后这一位也直接加减;
上一位进位后这一位多个1,然后直接加减;
上一位退位后这一位少个1,然后直接加减。
取最小值为直接加减次数,剩余的同理可得.
"""
dp[i][0] = min(dp[i-1][0]+abs(s1[i]-s2[i]),dp[i-1][1]+abs(s1[i]+1-s2[i]),dp[i-1][2]+abs(s1[i]-1-s2[i]))
dp[i][1] = min(dp[i-1][0]+10-s1[i]+s2[i],dp[i-1][1]+10-(1+s1[i])+s2[i],dp[i-1][2]+10-(s1[i]-1)+s2[i])
dp[i][2] = min(dp[i-1][0]+10+s1[i]-s2[i],dp[i-1][1]+10+(1+s1[i])-s2[i],dp[i-1][2]+10+(s1[i]-1)-s2[i])
ans = min(dp[n-1][0],dp[n-1][1],dp[n-1][2]) #取三者最小值即为答案
print(ans)
六、树上选点。
1. 考点:
2. 难度:
3. 要点分析
七、T字消除。
1. 考点:
2. 难度:
3. 要点分析
八、独一无二。
1. 考点:
2. 难度:
3. 要点分析
九、异或和。
1. 考点:DFS
2. 难度:⭐⭐⭐
3. 要点分析
①用“邻接表”保存结点关系(邻接矩阵稀疏,所占空间过大),对树“深度遍历”的同时进行计算。
②注意结点之间的父子关系,编号小的结点为编号大的结点的父结点。
import sys
def dfs(pos):
global ans
# 终止条件:叶子结点
if relations[pos]==[]:
return
for t in relations[pos]:
ans ^= weighted[t]
dfs(t)
# 输入数据
n, m = map(int, input().split())
weighted = [0] + [x for x in map(int, input().split())]
relations = [[] for _ in range(n+1)]
for i in range(n-1):
i, j = map(int, input().split())
if i<j:
relations[i].append(j)
else:
relations[j].append(i)
for i in range(m):
info = [int(x) for x in input().split()]
if info[0]==1:
weighted[info[1]] = info[-1]
elif info[0]==2:
ans = weighted[info[1]]
dfs(info[1])
print(ans)
十、混乱的数组。
1. 考点:思维、构造
2. 难度:⭐⭐⭐⭐
3. 要点分析