kkksc03考前临时抱佛脚
日期:2023.9.24
题目地址:kkksc03考前临时抱佛脚
题目分析
最近新学了回溯和动态规划,刚好就借着这个题来巩固一下。
用了两个个方法逐步优化一下,回溯搜索 -> 动态规划。
这个题目要求四科花的时间最少,就是相当于单独处理每一科的时间最少。
- 回溯搜索
这个其实就是有点像暴力枚举一样,考虑了所有答案,取最小的。也可以叫作深搜,这个方向就左脑和右脑两种,从左脑开始,先左后右,到头了就回到上一个状态,考虑了所有结果。题目数据量很小,但还是担心会不会超时。果然,超时了。只拿了90分。
- 动态规划
这个就可以看作01背包问题了,背包大小最大是 1200 容量,物品最多20个。
理想情况:最优的时候无疑就是左右脑各为总时间的一半 s u m / 2 sum/2 sum/2 。那么,我们就想办法让其中一个脑子往这一半靠,那么另一个脑子便是结果了。
这个解释一下,就是相当于,一个脑当背包耗时 v v v,那另一个脑的用时就是总用时减去前面那个脑的重量 s u m − v sum-v sum−v。最终用时就是 m a x ( v , s u m − v ) = s u m − v max(v, sum-v)= sum-v max(v,sum−v)=sum−v。因为 v < = s u m / 2 v<=sum/2 v<=sum/2。
不出意外,快了很多。
代码
- 回溯搜索
"""
回溯搜索
"""
s_li = [int(i) for i in input().split()]
"""
x:当前搜索的元素为第几个;
y:此时是第几科(总共4科);
left:记录左脑用时;
right:记录右脑用时;
minn:记录当前最小用时。
"""
def backsearch(x, y, left, right, minn):
# 当前科目的题目做完了, 记录最小时间。
if x+1 > s_li[y]:
# max(left, right)
# 选出最大值,因为左右脑只能同时在同一科目工作, 最大的即为当前科目的耗时。
minn = min(minn, max(left, right))
return minn
# 先左脑
left += a_li[x]
minn = backsearch(x+1, y, left, right, minn)
# 回溯
left -= a_li[x]
# 再右脑
right += a_li[x]
minn = backsearch(x+1, y, left, right, minn)
right -= a_li[x]
return minn
ans = 0
for i in range(4):
left = 0
right = 0
minn = 2e7
a_li = [int(i) for i in input().split()]
ans += backsearch(0, i, left, right, minn)
print(ans)
- 动态规划
"""
动态规划
"""
s_li = [int(i) for i in input().split()]
ans = 0
for i in range(4):
# 初始化
# 这个数组用来记录在当前科目下,总耗时不超过 k 时,一个脑子的最大耗时之和
dp = [0]*2501
a_li = [int(i) for i in input().split()]
summ = sum(a_li) # 总耗时
for j in range(s_li[i]):
for k in range(summ//2, a_li[j]-1, -1):
# 让这个脑子的用时尽可能的接近summ//2。
dp[k] = max(dp[k], dp[k-a_li[j]]+a_li[j])
# 另一个脑子的耗时便是结果
ans += summ - dp[summ//2]
print(ans)
这里还是解释一下动态规划的思想体现:
“”“
在每个 k 的位置,更新 dp[k] 的值。这里使用动态规划的思想,dp[k] 的值取两种情况中的最大值:
不选当前题目 j,即 dp[k] 保持不变。
选取当前题目 j,即 dp[k] 更新为 dp[k-a_li[j]] + a_li[j]。这表示当前总耗时 k 下,如果选择题目 j,那么这个脑的最大耗时之和就是 dp[k-a_li[j]](在不选题目 j 的基础上加上题目 j 的耗时 a_li[j])。
”“”
小结
当然,可能还有其他的优解,读者可以自己去试试呢。我也不知道我解释的清不清楚,有疑问欢迎评论交流。