1:2023
思路
只能想到纯纯暴力,枚举每一个数,统计每个数是否包含2023。
注意要算的是完全不包含2023的数目,别算反了。
代码
答案:85959030
# 完全不包含2023
def judge(x):
li = [3,2,0,2]; i=0
while x>0:
t = x%10; x//=10
if t==li[i]: i+=1
if i==4: return 0
return 1
if __name__=='__main__':
st = 12345678; ed = 98765432
res = 0
for x in range(st, ed+1):
res += judge(x)
print(res) # 85959030
2:硬币兑换
思路
能暴力出结果,但是我考虑错了。
我们要计算的是,任意次兑换后,某种硬币的数目达到最大。那么容易想到,只有把所有能兑换过去的硬币全部兑换过去,得到的目标硬币数才可能最大。
假设我们的目标硬币是面值为5(奇数),初始硬币数为5,并且面值分别为1+4、2+3的硬币可以兑换过来。假设目标硬币面值为4(偶数),初始硬币数为4,面值分别为1+3、2+2的硬币可以兑换过来。综上,我们可以枚举所有可能的目标硬币情况,对每一个面值的硬币分奇偶讨论他的最大数目,然后求出所有硬币的 max 值。
比赛时,我认为目标硬币面值的情况为1~2023,其实是想错了。应该是1–2023*2。要注意一点,当目标硬币的面值超过2023后,兑换前要考虑是不是coin1和coin2都有旧版硬币。
代码
答案:682425
def calcu(x):
cnt = 0
if x<=2023: cnt=x
if x%2>0: # 奇数
for i in range(1, x//2+1):
if x-i<=2023: cnt += i
else:
for i in range(1, x//2):
if x-i<=2023: cnt += i
if x//2<=2023: cnt += x//4
return cnt
if __name__=='__main__':
res = 0
for k in range(1,2023*2+1):
res = max(res, calcu(k))
print(res) # 682425
3:松散子序列
思路
这是一道线性dp题。考试时,我写了个二重循环(虽然也是dp的思想),估计只能过50%左右。
令f[i]
表示以a[i]
结尾的子序列的最大价值,由于松散子序列要求pi - pi-1 >= 2
,所以f[i] = max(f[j]) + a[i]
,j从0 ~ i-2
。最后答案是遍历所有f[i]
,找一个最大值。
这样写是一个二重循环,肯定会超时,所以要优化代码。我们发现f[i-2]
一定大于f[i-4]
,且f[i-3]
一定大于f[i-5]
,因此内层循环可以转化成只比较f[i-2]
和f[i-3]
的大小。
代码
if __name__=='__main__':
s = input(); l = len(s)
f = [0]*(l+5)
for i in range(l):
if i-2>=0: f[i] = max(f[i], f[i-2])
if i-3>=0: f[i] = max(f[i], f[i-3])
f[i] += ord(s[i])-ord('a')+1
res = 0
for i in range(l):
res = max(res, f[i])
print(res)
4:管道
思路
这是一道二分+区间合并的题目,但是鼠鼠想不出来,听了y哥的点拨写的。
二分法可以找到区间的边界点,如果我们以时间为轴,右侧区间的时间点满足管道中每一段中间的传感器都检测到有水流,左侧区间的时间点是管道中每一段中间的传感器不能都检测到水流,所以本题的要求就是求出右区间的边界点。二分法有很多需要注意的细节,所以我就直接背模板了。
在二分法里,对于每一个时间点,我们都要判断此时是否满足区间性质。可以算出该时间点,每一个阀门喷出的水能覆盖多长的区间。用区间合并算法合并这些区间,然后判断最后得到的区间是不是能覆盖所有的传感器。
要注意这题在区间合并部分不需要排序,如果加上排序,区间合并部分的时间复杂度就是 O ( n l o g n ) O(nlogn) O(nlogn),在new online judge上测会有一个样例超时。如果不排序的话,区间合并在模板的基础上就要改,不能有区间数目cnt的统计情况。因为题目里说 L i − 1 < L i L_{i-1}<L_i Li−1<Li,所以是可以证明不需要排序的。但鼠鼠就不写了。
代码
# 二分+区间合并
def check(t, l):
# 区间合并
le = len(li); ed = 0
for i in range(le):
if t-si[i]>=0:
a = max(li[i]-t+si[i], 1); b = min(li[i]+t-si[i], l)
if a<=ed+1:
ed = max(ed, b)
if ed==l: return True
else: return False
if __name__=='__main__':
n, ll = map(int, input().split())
li, si = [], []
for _ in range(n):
li_, si_ = map(int, input().split())
li.append(li_); si.append(si_)
# 二分
l, r = 0, int(2e9)
while l<r:
mid = (l+r)//2;
if check(mid, ll): r=mid
else: l=mid+1
print(l)
5:保险箱
思路
想了个bfs的写法,但是只过了一个样例。网上也找不到题解,放弃了。
6:树上选点
思路
以为自己省赛写对了,结果发现只过了一个样例。或许省赛我只有10多分吧。
后记
国赛考完了,以后有机会再填坑。