刷题链接:第2题-模型推理量化加速优化问题 - 题目详情 - CodeFun2000
为什么要用 np.minimum,用 min 不行吗?
-
min()是给"普通数字"或者"纯列表"准备的。 如果你写min(3, 5),它会返回 3。但如果你把两个数组 传给它,比如min([1, 5, 9], [2, 4, 6]),Python 底层会像查字典比较字母顺序一样去比较它们。因为它看到第一位1 < 2,它就会直接返回整个第一个数组[1, 5, 9]!它根本不会去管后面的 5 和 4 谁大谁小。 -
np.minimum()是专门给"数组进行单挑"准备的。 当你把两个 NumPy 数组交给它:np.minimum([1, 5, 9], [2, 4, 6])时,它会把两个数组并排摆在一起,逐个位置进行单挑:-
第 1 位:1 和 2 单挑,留下 1。
-
第 2 位:5 和 4 单挑,留下 4。
-
第 3 位:9 和 6 单挑,留下 6。
-
最终返回一个全新的数组:
[1, 4, 6]。
-
python
import sys
import numpy as np
def solve():
input_data = sys.stdin.read().split()
if not input_data:
return
L = int(input_data[0])
T = float(input_data[1])
'''
round(number, ndigits=None)
number:你要处理的数字。
ndigits:你要保留的小数位数。如果不填,默认返回整数。
'''
S=100
W = int(round(T*S))
layers = []
idx = 2
for _ in range(L):
K = int(input_data[idx])
idx +=1
options = []
for _ in range(K):
bit = input_data[idx]
idx +=1
loss = float(input_data[idx])
w = int(round(loss*S))
idx +=1
mem = float(input_data[idx])
idx+=1
options.append((w,mem))
layers.append(options)
INF = float('inf')
dp=np.full(W+1,INF,dtype=np.float64)
'''
numpy.full(shape, fill_value, dtype=None)
# 2. 创建二维数组,2 行 3 列,全部填满 3.14
b = np.full((2, 3), 3.14)
# 结果:
# [[3.14 3.14 3.14]
# [3.14 3.14 3.14]]
'''
dp[0]=0
for options in layers:
ndp = np.full(W+1,INF,dtype=np.float64) # 准备一本全新的记事本
# dp 是你上一关打完后,所有存活状态的最佳记录。
# ndp(new dp)是你这一关的全新记事本,刚开始全是无穷大 INF。
for w,men in options:
if w<=W:
candidate = dp[:W-w+1]+men
ndp[w:] = np.minimum(ndp[w:],candidate)
dp = ndp
ans = np.min(dp)
print(f"{ans:.2f}")
if __name__ =="__main__":
solve()
更易懂但会慢一点的代码写法:
python
import sys
def solve():
# ---------- 1. 读取数据(和之前一样) ----------
input_data = sys.stdin.read().split()
if not input_data:
return
L = int(input_data[0])
T = float(input_data[1])
S = 100
W = int(round(T * S)) # W 是我们能承受的最大伤害总额
layers = []
idx = 2
for _ in range(L):
K = int(input_data[idx])
idx += 1
options = []
for _ in range(K):
bit = input_data[idx]
idx += 1
loss = float(input_data[idx])
w = int(round(loss * S))
idx += 1
mem = float(input_data[idx])
idx += 1
options.append((w, mem))
layers.append(options)
# ---------- 2. 准备初始记事本 ----------
INF = float('inf')
# 不用 Numpy,用纯 Python 列表创建一个有 W+1 个格子的记事本,全填满 INF
dp = [INF] * (W + 1)
dp[0] = 0.0 # 开局:受 0 点伤害,花 0 个金币
# ---------- 3. 核心大循环(极其直白的三层嵌套) ----------
# 第一层循环:一关一关地打
for options in layers:
# 每到新的一关,拿出一个全是 INF 的新记事本
ndp = [INF] * (W + 1)
# 第二层循环:看看这一关商店里卖哪些装备
for w, mem in options:
# 第三层循环:最关键的一步!
# 我们挨个去翻看"上一关的旧记事本(dp)",看看里面哪些状态是活着的。
for prev_w in range(W + 1):
# 如果这个柜子里装的不是 INF,说明这是一个"真实的存活状态"
if dp[prev_w] != INF:
# 尝试穿上新装备:算算新伤害和新花费
curr_w = prev_w + w
curr_mem = dp[prev_w] + mem
# 如果穿上后,总伤害没有超过我们的上限 W
if curr_w <= W:
# 那么,我们就看看这个总伤害对应的新柜子里,能不能放下更便宜的价格
# 如果当前的花费比新柜子里原有的花费更少,就替换它!(这就是找最便宜的)
if curr_mem < ndp[curr_w]:
ndp[curr_w] = curr_mem
# 这一关商店里的装备全试完了,交接记事本
dp = ndp
# ---------- 4. 输出结果 ----------
# 找遍打完最后一关的记事本,看看哪个伤害值下,我们花的钱最少
ans = min(dp)
if ans == INF:
print("No valid solution")
else:
print(f"{ans:.2f}")
if __name__ =="__main__":
solve()