原题地址 。
— 第 3 天:大厅 —
你走下短楼梯,进入大得惊人且空无一人的大厅,并迅速通过了安全检查点。然而,当你到达主电梯时,你发现每个电梯上方都亮着红灯:它们都离线了。
"抱歉,"一位精灵一边摆弄着附近的控制面板一边道歉。“某种电涌似乎把它们烧坏了。我会尽快让它们上线。”
你解释了你需要继续深入地下。“嗯,你至少可以乘自动扶梯下到印刷部门,不过没有电梯工作你也无法走得更远。也就是说,如果自动扶梯没有离线的话,你是可以的。”
“但是,别担心!它没坏;只是需要电力。也许你可以在我继续修理电梯的时候让它运转起来。”
附近有电池可以为自动扶梯提供紧急电力,正是为了应对这种情况。每个电池都标有它们的伏特等级,一个从 1 到 9 的值。你记下了它们的伏特等级(你的谜题输入)。例如:
987654321111111
811111111111119
234234234234278
818181911112111
电池被排列成组;你输入的每一行数字对应一组电池。在每组内,你需要恰好打开两个电池;该组产生的伏特数等于你所打开电池上的数字组成的数字。例如,如果你有一组 12345 并且你打开了第 2 和第 4 个电池,该组将产生 24 伏特。(你不能重新排列电池。)
你需要找出每组电池能产生的最大可能伏特数。在上面的例子中:
- 在
987654321111111中,你可以通过打开前两个电池来产生最大可能的伏特数 98。 - 在
811111111111119中,你可以通过打开标有 8 和 9 的电池来产生最大可能的伏特数,产生 89 伏特。 - 在
234234234234278中,你可以通过打开最后两个电池(标记为 7 和 8)来产生 78。 - 在
818181911112111中,你可以产生的最大伏特数是 92。
总输出伏特数是每组最大伏特数的总和,所以在这个例子中,总输出伏特数是 98 + 89 + 78 + 92 = 357。
你面前有很多电池。找出每组可能的最大伏特数;总输出伏特数是多少?
— 第二部分 —
自动扶梯没有动。精灵解释说,它可能需要更多的伏特来克服系统的静摩擦力,并按下了一个大大的红色"伏特限制安全覆盖"按钮。你数不清她需要确认多少次"是的,我确定",于是你一边等待一边稍微装饰了一下大厅。
现在,你需要在每个电池组中恰好打开十二个电池来产生最大的伏特数。
每个电池组的伏特输出仍然由你打开的电池上的数字组成;唯一的不同是,现在每个电池组的伏特输出将有 12 位数字,而不是两位。
再考虑一下之前的例子:
987654321111111
811111111111119
234234234234278
818181911112111
现在,伏特数变得大得多:
- 在
987654321111111中,可以通过打开除末尾的一些 1 之外的所有电池来产生最大的伏特数987654321111。 - 在数字序列
811111111111119中,可以通过打开除一些 1 之外的所有电池来产生最大的伏特数811111111119。 - 在
234234234234278中,可以通过打开除开头附近的一个 2、一个 3 和另一个 2 电池之外的所有电池来产生最大的伏特数434234234278。 - 在
818181911112111中,通过打开除开头附近的一些 1 之外的所有电池,产生了伏特数888911112111。
现在总输出伏特数要大得多:987654321111 + 811111111119 + 434234234278 + 888911112111 = 3121910778619。
新的总输出伏特数是多少?
解题思路:
第一部分很简单,用笛卡尔积就能解决,一个表提供十位数,另一个提供个位数,合并后取最大值即可。
with recursive t as(select '987654321111111
811111111111119
234234234234278
818181911112111' t),
b as(select row_number()over()rn,b from(select unnest(string_split(t,chr(10)))b from t))
,c as(select rn, row_number()over()rn2,b from(select rn,unnest(string_split(b,''))b from b))
,d as(select max(c.b||c2.b)maxb2 from c,c c2 where c.rn=c2.rn and c.rn2<c2.rn2 group by c.rn)
select sum(maxb2::int) from d;
鉴于正式数据每行有100个数字,第二部分用笛卡尔积硬算所有的排列不切实际,需要更好的算法。
我向DeepSeek求助,他提供了如下算法实现:
好的,我们先明确题意:
有一个 100 位正整数,每位都是 1~9 之间的数字,我们要保持数字的相对顺序(即从左到右挑出 10 个数字,不能改变在原数字中的前后关系),组成一个 10 位数,并且让这个 10 位数尽可能大。
这是一个在数字序列中按顺序取 k 个数使其最大的经典问题,可以用贪心 + 单调栈的思路解决,复杂度 O(n)。
贪心思路
给定数字序列 num(长度为 n=100),要选 k=10 位,使最终的数字最大。
算法步骤:
- 维护一个栈(列表),用于存放最终选的数字。
- 遍历
num的每一位数字ch(字符):- 如果栈非空,且栈顶的数字比
ch小,并且后面还有足够的数字使得即使 pop 掉栈顶,最终还能选满 k 位,则弹出栈顶(因为选后面更大的数字更优)。 - 否则,将
ch入栈。
- 如果栈非空,且栈顶的数字比
- 注意:我们要选 k=10 位,但最后栈的长度可能超过 k,因为不能无限制 pop,必须保证剩余足够多的数字来凑满 k。
- 遍历完整个
num后,取栈的前 k 位作为结果(因为如果栈长度 > k,说明后面数字太小无法替换前面的,就直接取前 k 位;但按照经典算法,长度一般是等于 k)。
具体来说,“剩余足够的数字”条件为:
栈长度 + (n - 当前索引 - 1) >= k
即当前已处理的数字位置为 i(0-based),n-i-1 是后面还未处理的数字个数,加上栈里已有的数字,必须保证最后能凑齐 k 个。
Python 代码实现
def largest_number_from_digits(num_str, k):
stack = []
n = len(num_str)
for i, ch in enumerate(num_str):
# 当栈非空,且栈顶小于当前数字,且还有足够剩余数字可以凑齐k位时
while stack and stack[-1] < ch and len(stack) + (n - i - 1) >= k:
stack.pop()
if len(stack) < k:
stack.append(ch)
# 栈的长度正好是k,直接组合
return ''.join(stack)
result = largest_number_from_digits(num_str, 10)
print("选出的最大10位数:", result)
用下述语句把它定义为duckdb自定义函数, 然后对每行字符串都执行这个函数即可。
import duckdb
from duckdb.typing import *
def largest_number_from_digits(num_str: str, k:int) -> str | None:
if not num_str:
return None
stack = []
n = len(num_str)
for i, ch in enumerate(num_str):
# 当栈非空,且栈顶小于当前数字,且还有足够剩余数字可以凑齐k位时
while stack and stack[-1] < ch and len(stack) + (n - i - 1) >= k:
stack.pop()
if len(stack) < k:
stack.append(ch)
# 栈的长度正好是k,直接组合
return ''.join(stack)
duckdb.create_function(
"largest_number_from_digits",
largest_number_from_digits,
null_handling="special"
)
num_str ='15162378'
result = largest_number_from_digits(num_str, 3)
res = duckdb.sql("SELECT largest_number_from_digits('15162378', 3)").fetchall()
print(res)
s="""
with recursive t as(select '987654321111111
811111111111119
234234234234278
818181911112111' t),
b as(select row_number()over()rn,b from(select unnest(string_split(t,chr(10)))b from t))
select sum(largest_number_from_digits(b, 12)::bigint) from b;
"""
res = duckdb.sql(s).fetchall()
print(res)
以上代码适用于DuckDB v1.3.2。
执行结果如下
python 2503udf.txt
[('678',)]
[(3121910778619,)]

545

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



