写在前面
题解全由我本人实际刷题后写的代码,题解能够保证提交后一定正确无误,若有更优解可以在评论区@我,不胜感激。
A 弹珠堆放
这是一个填空题,直接写答案的。
而且非常的简单,如果弹珠目前是第n
层,那么最底部会比它的上面一层多n
个弹珠。
写一个小模拟就可以得到答案了。
cnt = 1
res = 1
for i in range(2, 500):
cnt += i
res += cnt
if res > 20230610:
print(i, res)
break
# 494
print(494)
B 划分
题目大意就是将40个数分成两组数的和相乘,我们需要先知道一个数学定理,就是这两组数的差值越小它们乘积越大。
所以我们的初步想法就是给这40个数分组的时候要均匀分配,具体的解决方案我们可以定一个dp
数组,先对40个数求和然后取一半的值作为一个背包的容量,然后就转换成了一个类似背包的问题,dp
数组的值只能为0或1,代表能否取到该值。具体细节参考代码。
cnt1, cnt2 = 0, 0
a = [5160, 9191, 6410, 4657, 7492, 1531, 8854, 1253, 4520, 9231, 1266,
4801, 3484, 4323, 5070, 1789, 2744, 5959, 9426, 4433, 4404, 5291,
2470, 8533, 7608, 2935, 8922, 5273, 8364, 8819, 7374, 8077, 5336,
8495, 5602, 6553, 3548, 5267, 9150, 3309]
N = sum(a) // 2 + 1
dp = [0] * N
dp[0] = 1
for i in range(40):
for j in range(N):
dp[j] = max(dp[j - a[i]], dp[j])
tag = 0
for i in range(N-1, -1, -1):
if dp[i] == 1:
tag = i
break
print(tag * (sum(a) - tag))
print(12873625444)
C 偶串
这道编程题比较简单,不知道国赛考这个是不是怕我们爆零。
用一个字典统计每个字母出现的个数,然后再遍历,如果有出现奇数个次数的字母就输出NO
。
d = {}
a = list(input())
for i in a:
if i not in d:
d[i] = 1
else: d[i] += 1
flag = True
for i in d.values():
if i&1: flag = False
if flag: print('YES')
else: print('NO')
D 交易账本
我承认上一道编程题我说话声音比较大,这一道题的题面也太长了吧,这不纯纯考我们的阅读理解吗?
我自己第一次做的时候做的我心烦气躁,但是认真读完题目,按照要求来模拟还是挺好做的。
这题还需要注意几个关键信息,比如我们要记录每次输出的编号次序以及金额,然后输入的时候要把对应输出的金额全部用上。
N = 1000 + 10
for _ in range(int(input())):
n, m = map(int, input().split())
flag = True # 账本合理性的标记
outMessge = [[0] * N for _ in range(N)] # 记录交易记录的输出金额
for id in range(m):
op = list(map(int, input().split()))
# 输入部分
inMoney = 0
inCount = op[0]
for i in range(1, 2 * inCount + 1, 2):
fromId, fromOutNumber = op[i], op[i + 1]
if fromId > id: # 输入来自于未来的输出,不合理
flag = False
if fromId != -1:
inMoney += outMessge[fromId][fromOutNumber]
outMessge[fromId][fromOutNumber] = 0 # 清空输入中的金额
# 输出部分
outCount = op[2 * inCount + 1]
outNum, outMoney = 0, 0
for i in range(2 * inCount + 2, 2 * inCount + 2 * outCount + 2, 2):
acount, val = op[i], op[i + 1]
outMessge[id][outNum] += val # 记录输出的信息
outMoney += val
outNum += 1
if fromId != -1 and inMoney != outMoney: # 输出的金额不等于输入的金额
flag = False
if flag: print('YES')
else: print('NO')
E 背包问题
看到1000的数据量级,三重背包肯定是超空间的。观察数据量只有 c n t A cnt_A cntA和 c n t B cnt_B cntB比较小,所以可以枚举积木的数量,因此关键点在于枚举 A A A积木在背包1和背包的数量。
for _ in range(int(input())):
B1, B2, B3 = map(int, input().split())
cntA, cntB = map(int, input().split())
vA, vB = map(int, input().split())
if vA > vB: # 确保积木A的体积比B的体积小
cntA, cntB = cntB, cntA
vA, vB = vB, vA
ans = 0
for i in range(cntA + 1):
if i * vA > B1:
break
for j in range(cntA - i + 1):
if j * vA > B2:
break
res = i + j # res存已经放了多少积木数
remainCntA = cntA - i - j
remainCntB = cntB
remain_B1 = B1 - vA * i
remain_B2 = B2 - vA * j
remain_B3 = B3
# 将B放入背包1和背包2中
res += min(remain_B1 // vB, cntB) # 将背包1的剩余空间用B积木尽量装
remainCntB -= min(remain_B1 // vB, remainCntB)
res += min(remain_B2 // vB, remainCntB)
remainCntB -= min(remain_B2 // vB, remainCntB)
# 剩下的背包3先用余下的A装
res += min(remain_B3 // vA, remainCntA)
remain_B3 -= min(remain_B3 // vA, remainCntA) * vA
# 再用B补充余下的背包空间
res += min(remain_B3 // vB, remainCntB)
ans = max(ans, res)
print(ans)
F 翻转
典型的动态规划题, d p i , j dp_{i,j} dpi,j表示处理到第 i i i个字符串,结尾是 j j j字母的最小长度。
def tfm(s, i): # a~z对应0~25
return ord(s[i]) - ord('a')
n = int(input())
a = [0]
for i in range(n): a.append(input())
dp = [[0x3f3f3f3f] * 26 for _ in range(n + 1)]
dp[1][tfm(a[1], 0)] = dp[1][tfm(a[1], 1)] = 2
for i in range(2, n + 1):
for j in range(26):
A, B = tfm(a[i], 0), tfm(a[i], 1)
# 不翻转 'AB'
if A == j: dp[i][B] = min(dp[i][B], dp[i - 1][j] + 1)
else: dp[i][B] = min(dp[i][B], dp[i - 1][j] + 2)
# 翻转的情况 'BA'
if B == j: dp[i][A] = min(dp[i][A], dp[i - 1][j] + 1)
else: dp[i][A] = min(dp[i][A], dp[i - 1][j] + 2)
ans = 0x3f3f3f3f
for i in range(26):
ans = min(ans, dp[n][i])
print(ans)
G 最大阶梯
这道题我第一思路是对整体阶梯图形做hash
然后比对,发现很难实现。那就想了一个dfs
去暴力,如果加一个记忆化就差不多是一个动态规划。
每一个阶梯图形都可以从一个底端点延申过来,我们对图像都考虑成下三角的形式,所以
d p i , j = m i n ( d p i + 1 , j , d p i , j + 1 ) + 1 dp_{i,j}=min(dp{i+1,j},dp_{i,j+1})+1 dpi,j=min(dpi+1,j,dpi,j+1)+1
能转移的前提是它们直接的颜色相同,否则就是1。
def update(G):
global ans
dp = [[1] * (h + 1) for _ in range(h + 1)]
for i in range(h - 2, -1, -1):
for j in range(h - 2, -1, -1):
if G[i][j] == G[i + 1][j] == G[i][j + 1]:
dp[i][j] = min(dp[i + 1][j], dp[i][j + 1]) + 1
ans = max(ans, dp[i][j])
def turn(G):
new_G = [[0] * h for _ in range(h)]
for i in range(h):
for j in range(h):
new_G[j][h - i - 1] = G[i][j]
return new_G
h = int(input())
G = [list(map(int, input().split())) for _ in range(h)]
ans = 0
update(G)
for _ in range(3):
G = turn(G) # 让G顺时针转90°
update(G)
print(ans)
H 最长回文前后缀
这题看似简单,其实算字符串 h a s h hash hash正与反的位置非常的烦,这个我用了一个贪心思路,结果错了,只能过50%。
def hash(s):
n = len(s)
h = [0] * n
for i in range(1, n):
h[i] = (h[i - 1] * seed + ord(s[i])) % mod
return h
def query(h, l, r):
return (h[r] - h[l - 1] * pow(seed, r - l + 1)) % mod
seed = 233
mod = 10**9 + 7
s = list(input())
s1, s2 = [0] + s, [0] + s[::-1]
h1, h2 = hash(s1), hash(s2)
l, r = 0, len(s) + 1
while l + 1 != r:
mid = (l + r) // 2
if query(h1, 1, mid) == query(h2, 1, mid):
l = mid
else: r = mid
extra = 0
s3, s4 = [0] + s1[l+1:], [0] + s1[l+1:][::-1]
h3, h4 = hash(s3), hash(s4)
for i in range(1, len(s3)):
if query(h3, 1, i) == query(h4, len(s3) - i, len(s3) - 1):
extra = i
print(l * 2 + extra)
I 贸易航线
背包容量 k k k先可以不看,在途中如果一个商品的利润很大的话,我们把所有的背包容量去装那个利润很大商品即可。所以就相当于背包容量为1的时候,我们去解决问题,最最后将答案乘上 k k k即可。
因此 d p i , j dp_{i,j} dpi,j可表示为途径 i i i地背包里装的是 j j j物品时最大的收益, j j j为0的时代表当前背包为空。
状态转移方程如下:
- 到达 i i i地的时候,可以继承 i − 1 i-1 i−1地的状态,即$dp_{i,j} = d p i − 1 , j dp_{i - 1,j} dpi−1,j。
- 如果 j j j商品在当地可以售卖, d p i , 0 = m a x ( d p i , 0 , d p i , j + g i , j ) dp_{i,0} = max(dp_{i,0}, dp_{i,j} + g_{i,j}) dpi,0=max(dpi,0,dpi,j+gi,j)
- 如果 j j j商品在当地可以进货, d p i , j = m a x ( d p i , j , d p i , 0 − g i , j ) dp_{i,j} = max(dp_{i,j}, dp_{i,0} - g_{i,j}) dpi,j=max(dpi,j,dpi,0−gi,j)
dp = [[-10**18] * 11 for _ in range(10**5 + 1)]
g = [0] * (10**5 + 1)
n, m, k = map(int, input().split())
for i in range(1, n + 1):
g[i] = [0] + list(map(int, input().split()))
dp[0][0] = 0
for i in range(1, n + 1):
for j in range(0, m + 1):
dp[i][j] = dp[i - 1][j]
for j in range(1, m + 1):
if g[i][j] != -1:
dp[i][0] = max(dp[i][j] + g[i][j], dp[i][0])
for j in range(1, m + 1):
if g[i][j] != -1:
dp[i][j] = max(dp[i][0] - g[i][j], dp[i][j])
print(dp[n][0] * k)
J 困局
待更新…