原题地址 。
— 第 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就行了。
代码如下
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,他提供了如下代码
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结合数组实现,以下是我翻译的代码,注意数组合并||要求左右值均是数组。
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))
;

686

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



