利用Duckdb求解Advent of Code 2025第5题 自助餐厅

原题地址 。

--- 第 5 天:自助餐厅 ---

当叉车打通墙壁时,精灵们高兴地发现,另一边确实有一个自助餐厅。

你能听到厨房传来的喧闹声。"照这样下去,我们就没有时间在餐厅挂花环了!"你决心继续你的任务,于是前去查看。

"要是我们没有在圣诞节前换用新的库存管理系统就好了!"另一位精灵大声说道。你询问发生了什么事。

厨房里的精灵解释了情况:由于他们复杂的新库存管理系统,他们无法分辨哪些食材是新鲜的,哪些是变质的。当你询问系统如何工作时,他们给了你一份他们的数据库副本(你的谜题输入)。

该数据库基于食材ID运作。它由一个新鲜食材ID范围 列表、一个空行和一个可用食材ID列表组成。例如:

复制代码
3-5
10-14
16-20
12-18

1
5
8
11
17
32

新鲜ID范围是包含性 的:范围 3-5 意味着食材ID 3、4 和 5 都是新鲜的。范围也可以重叠;如果一个食材ID位于任何范围内,它就是新鲜的。

精灵们试图确定哪些可用食材ID是新鲜的。在这个例子中,判断如下:

  • 食材ID 1 是变质的,因为它不在任何范围内。
  • 食材ID 5 是新鲜的,因为它在范围 3-5 内。
  • 食材ID 8 是变质的。
  • 食材ID 11 是新鲜的,因为它在范围 10-14 内。
  • 食材ID 17 是新鲜的,因为它在范围 16-20 内,同时也在范围 12-18 内。
  • 食材ID 32 是变质的。

所以,在这个例子中,3 个可用食材ID是新鲜的。

处理来自新库存管理系统的数据库文件。有多少个可用食材ID是新鲜的?

--- 第二部分 ---

精灵们开始把变质的库存搬到厨房后面的垃圾滑道。

为了让它们在收到新库存时不再烦你,精灵们想知道所有被新鲜食材ID范围认为是新鲜的ID。如果一个食材ID位于任何范围内,它仍然被认为是新鲜的。

现在,数据库的第二部分(可用食材ID)已经无关紧要。以下是来自上面示例的新鲜食材ID范围:

复制代码
3-5
10-14
16-20
12-18

这些范围认为新鲜的食材ID是 3, 4, 5, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20。因此,在这个例子中,新鲜食材ID范围认为总共有 14 个食材ID是新鲜的。

再次处理数据库文件。根据新鲜食材ID范围,有多少个食材ID被认为是新鲜的?

解题方法:

第一部分很简单,就是求哪些食材在新鲜食材范围中,用数据库exists就行了。

代码如下

sql 复制代码
with t as (select '3-5
10-14
16-20
12-18

1
5
8
11
17
32' t),
a as(select b::bigint a from(select unnest(string_split(t,chr(10)))b from t)where instr(b,'-')=0 and b<>''),
r as(select substr(b,1,instr(b,'-')-1)::bigint l,substr(b,instr(b,'-')+1)::bigint r from(select unnest(string_split(t,chr(10)))b from t)where instr(b,'-')>0)
select count(*) from a where exists(select 1 from r where a.a between l and r);

第二部分涉及新鲜范围中的食材总数,因为正式数据很大,所以不可能把它们都展开再去重计数,而要采取范围上下限差值加总的办法,但要求这些范围都是彼此不重叠的。

SQL没有简单的操作可以实现范围合并,于是请教DeepSeek,他提供了如下代码

python 复制代码
def merge_intervals(intervals):
    """
    合并重叠区间,直到所有区间互不重叠。
    
    参数:
    intervals: 区间列表,例如 [[1,4], [2,7], [9,10]]
    
    返回:
    合并后的区间列表,例如 [[1,7], [9,10]]
    """
    if not intervals:
        return []
    
    # 按区间起点排序
    intervals.sort(key=lambda x: x[0])
    
    merged = []
    
    for interval in intervals:
        # 如果merged为空,或者当前区间与上一个区间不重叠
        if not merged or merged[-1][1] < interval[0]:
            merged.append(interval)
        else:
            # 有重叠,合并区间(取右端点的最大值)
            merged[-1][1] = max(merged[-1][1], interval[1])
    
    return merged

# 测试
intervals = [[1, 4], [2, 7], [9, 10]]
result = merge_intervals(intervals)
print(result)  # 输出: [[1, 7], [9, 10]]

这个算法简单,可以用递归CTE结合数组实现,以下是我翻译的代码,注意数组合并||要求左右值均是数组。

sql 复制代码
with recursive t as (select '3-5
10-14
16-20
12-18' t),
r as(select substr(b,1,instr(b,'-')-1)::bigint l,substr(b,instr(b,'-')+1)::bigint r from(select unnest(string_split(t,chr(10)))b from t)where instr(b,'-')>0)
,ar as(select list([l,r] order by l)ar from r)
,a as(select 2 lv ,[ar[1]] mg from ar 
union all
select lv+1, case when len(mg)=0 or mg[-1][2] < ar[lv][1] then mg||[ar[lv]] else mg[:len(mg)-1]::BIGINT[][]||[[mg[-1][1] , greatest(mg[-1][2], ar[lv][2]) ]]end  from a,ar where lv<=len(ar))
select sum(b[2]-b[1]+1) from(select unnest(mg) b from a where lv=(select max(lv) from a))
;
相关推荐
夜不会漫长32 分钟前
C++:红黑树
数据结构·算法
共享家95271 小时前
408 操作系统第一章
服务器·数据库·windows
AI备案指南-满满1 小时前
算法备案审核不通过,一般是什么原因?
java·人工智能·算法·大模型备案·算法备案·大模型登记
这个DBA有点耶1 小时前
连拿六年第一后宣布出海:国产数据库凭什么敢撕掉“平替”的标签?
数据库·人工智能·架构
Ramble_Naylor1 小时前
Rust深度解读:Send 与 Sync 的本质与线程安全边界
python·算法·rust
秋天的怀念_David1 小时前
算法一千零一夜 · 第二十三夜 电力民生写成的曲线
java·程序人生·算法
不一样的故事1261 小时前
NB/T 47013.8-2015 附录D 吸枪技术
人工智能·经验分享·算法
通问AI2 小时前
用 200 行 Python 给文档库做「上下文完整度」自检:四个可复算的检查项
算法
揽星河..2 小时前
【C语言】扫雷游戏
c语言·算法·游戏
云和恩墨2 小时前
数据库监控简史:从会回答到能交付——AI-DBA如何成为真正的数字同事
数据库·人工智能·dba