这个用SQL实现暂时有困难,所以先清理思路,然后告诉DeepSeek按此编写。
对第9题第二部分的分析如下:依据此思路编写python程序,不做别的。
约束条件
- 只移动文件,新位置终点< 原位置起点,文件按编号从大到小移动,所以无需考虑文件移动后腾出的空位
- 一个空位可能用多次,只要 放入的文件总长度<=空位长度,因为文件没有移动二次的,同一个空位长度始终递减
实现
设置2个表,一个文件表,一个空位表,记录编号,起始终止位置,数据结构:4个数组fs, fe, ss,se,下标就是编号
每次移动,记录文件新位置,更新空位表被填入的编号新起始终止位置
外循环:文件ID递减就是从fs, fe倒序
内循环:空位ID递增就是从ss, se正序,两种情况:当se>=fs时跳出,当找到合适空位填充完成跳出
推论:循环终止条件,当前文件位置左侧没有空位
或者 当前文件左侧文件(ID比它小)的长度都比左侧空位长度大,设置左侧文件长度最大值和空位最大值变量跟踪,也可考虑设置每种长度空位的最左编号变量跟踪,减少无谓搜索
计算校验和只要对文件表计算,对fs,fe处理,下标*(终点-起点+1)累计求和
奇怪的是,他好像没看懂,还是按照自己的理解,按题目描述那样先把紧缩格式还原为文件块和空闲块实际布局,被我否定了。
你无需按原题实现blocks,只要按我上个提示中的分析思路编写即可,列表也无需动态增删元素,只要按下标操作即可。
他终于明白,
好的,我理解你的意思:我们不需要实际维护 blocks 列表,而是用四个数组 fs, fe, ss, se 分别表示文件和空闲区的起止位置,通过更新这些数组来模拟移动过程,最后计算校验和。
这次的程序可以运行,用时2秒,已经快于我在网上找的python程序。而且创造性地利用了等差数列求和公式计算校验和,避免逐个位置累加。
C:\d>timer64 python ds2409b.py
6363913128533
Kernel Time = 0.015 = 0%
User Time = 2.109 = 99%
Process Time = 2.125 = 100% Virtual Memory = 10 MB
Global Time = 2.121 = 100% Physical Memory = 15 MB
它关键的循环存在一个明显的可优化之处。
# 2. 按文件ID从大到小移动
for fid in range(n_files - 1, -1, -1):
length = fe[fid] - fs[fid] + 1
# 找最左边的能容纳的空闲区
for si in range(n_spaces):
if se[si] < fs[fid] and se[si] - ss[si] + 1 >= length:
继续提示
因为文件id永远大于空闲区id,位置也是,所以我把内循环改为
for si in range(fid): if se[si] - ss[si] + 1 >= length:其他没改,就从2秒多减少到0.9秒,请在此基础上,实现我说的设置每种长度空位的最左编号变量跟踪,这样range的下限还能改大,减少循环次数
他说
好的,我们可以维护一个字典 min_space_index,记录每种长度对应的最左空闲区的索引。这样在查找时可以直接从可能的最小索引开始。
改回来的程序确实快了很多,但结果不对。经过调试,初始化字典没问题,动态更新字典逻辑有误。去掉动态更新字典部分,比不用字典的版本也有所提高,用时0.742秒。
继续提示
更新最左最小索引有错误,比如2没有移动到新腾出的空闲位置,请修改
结果越改越慢,要3秒多
继续提示
这个版本比不更新索引更慢,当往一个空闲块填文件后,只有两种会受影响,1.原长度最左索引必然改变,2.新长度最左索引可能改为它,其他不受影响,所以不用全都更新,更新方法:把原长度最左索引改为这个块右边第一个等于原长度空闲,跳出循环。比较新长度最左索引,如果位置在当前块左,才更新,只提供过程中
update_min_space_index的修改版本,初始化min_space_index不用变。其他代码也无需给出
他的输出
def update_min_space_index_optimized(changed_si, old_length, new_length):
"""优化更新:只更新受影响的长度"""
# 1. 更新原长度 old_length 的最左索引
if old_length in min_space_index and min_space_index[old_length] == changed_si:
# 需要重新找 old_length 的最左索引
new_min_index = n_spaces
for si in range(changed_si + 1, n_spaces):
if ss[si] <= se[si] and se[si] - ss[si] + 1 >= old_length:
new_min_index = si
break
min_space_index[old_length] = new_min_index
# 2. 如果新长度 > 0,更新新长度 new_length 的最左索引
if new_length > 0:
if new_length not in min_space_index or min_space_index[new_length] > changed_si:
min_space_index[new_length] = changed_si
在移动文件后的更新部分这样调用:
# 更新空闲区
old_space_length = se[found_si] - ss[found_si] + 1
if old_space_length > length:
# 部分使用
ss[found_si] = new_end + 1
new_space_length = old_space_length - length
update_min_space_index_optimized(found_si, old_space_length, new_space_length)
else:
# 完全用尽
ss[found_si] = se[found_si] + 1
update_min_space_index_optimized(found_si, old_space_length, 0) # 新长度为0表示无效
按他说的改完后结果正确,用时还是0.7秒多,没有变化。
我把它的更新索引函数的关键语句复制粘贴到原有代码中,而不用函数调用,结果很好,一下子降到0.056秒。经过检查,文件和空闲块循环次数从25334129降到了14515。
最终代码如下
# Standard Library
import pathlib
def get_input(filename: str) -> list[str]:
"""
Takes a filename and returns a list of lines from the file
"""
output = []
file = pathlib.Path(filename)
with open(file) as f:
for line in f:
tmp_line = line.strip()
output.append(tmp_line)
return output
def solve():
import sys
data = get_input("2409-input.txt")[0] #sys.stdin.read().strip()
# 1. 解析得到 files 和 spaces
fs, fe = [], []
ss, se = [], []
pos = 0
is_file = True
file_id = 0
for ch in data:
length = int(ch)
if is_file:
fs.append(pos)
fe.append(pos + length - 1)
file_id += 1
else:
ss.append(pos)
se.append(pos + length - 1)
pos += length
is_file = not is_file
n_files = len(fs)
n_spaces = len(ss)
# 2. 初始化 min_space_index
min_space_index = {}
for length in range(1, max((se[i] - ss[i] + 1) for i in range(n_spaces)) + 1):
# 找到第一个长度 >= length 的空闲区
for si in range(n_spaces):
if se[si] - ss[si] + 1 >= length:
min_space_index[length] = si
break
else:
min_space_index[length] = n_spaces # 表示不存在
cnt=0
# 3. 按文件ID从大到小移动
for fid in range(n_files - 1, -1, -1):
length = fe[fid] - fs[fid] + 1
# 确定搜索起点
start_si = min_space_index.get(length, n_spaces)
if start_si >= n_spaces:
continue
# 查找合适的空闲区
found_si = -1
for si in range(start_si, fid): # 只搜索文件左边的空闲区
cnt+=1
if ss[si] > se[si]: # 无效空闲区
continue
if se[si] < fs[fid] and se[si] - ss[si] + 1 >= length:
found_si = si
break
if found_si == -1:
continue
# 移动文件
cnt+=1
new_start = ss[found_si]
new_end = new_start + length - 1
fs[fid], fe[fid] = new_start, new_end
# 更新空闲区
old_space_length = se[found_si] - ss[found_si] + 1
if old_space_length > length:
# 部分使用
ss[found_si] = new_end + 1
new_space_length = old_space_length - length
# 更新 min_space_index[old_space_length],因为该空闲区变小,不符合要求
for si in range(found_si + 1, fid):
if ss[si] <= se[si] and se[si] - ss[si] + 1 >= old_space_length:
min_space_index[old_space_length] = si
break
# 更新 min_space_index[new_space_length],因为该空闲区变小后比现有的min_space_index[new_space_length]更左
if min_space_index[new_space_length] > found_si:
min_space_index[new_space_length] = found_si
else:
# 完全用尽
ss[found_si] = se[found_si] + 1 # 标记无效
# 更新 min_space_index[old_space_length],因为该空闲区消失
for si in range(found_si + 1, fid):
if ss[si] <= se[si] and se[si] - ss[si] + 1 >= old_space_length:
min_space_index[old_space_length] = si
break
# 4. 计算校验和
checksum = 0
for fid in range(n_files):
start, end = fs[fid], fe[fid]
#print(fid, start, end)
checksum += fid * (start + end) * (end - start + 1) // 2
print(checksum, cnt)
if __name__ == "__main__":
solve()


被折叠的 条评论
为什么被折叠?



