SDE / Intern · Coding / OA

Amazon OA:稀疏袋子区间,长度 K 窗口最大价值

将闭区间改为半开边界,前缀质量积分查询窗口,枚举所有斜率变化点。

题目重点:将闭区间改为半开边界,前缀质量积分查询窗口,枚举所有斜率变化点。

题意与假设

这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。

  • 坐标为整数,闭区间[l,r]含r-l+1个袋,value非负,区间间隙价值0。
  • 窗口可放任何整数起点,长度k>0;区间不能重叠,空输入返回0。

解题路线

  1. 不能展开所有坐标,巨大间隙可能使空间爆炸。将每段变[l,r+1),前缀数组存完整段质量(length·value)。
  2. 定义F(x)=坐标严格小于x的价值总和。二分定位最后start≤x的段,完整此前质量加当前段截断长度;x落间隙时截到段end即可。窗口[s,s+k)价值F(s+k)-F(s)。
  3. F是连续分段线性,斜率仅在每段start与end改变。窗口价值斜率改变只可能在这些edge或edge-k,因此最大值在某个候选边界达到;平坦最大区间同样可以取端点。
  4. 枚举所有edge和edge-k,用两个F查询评价,O(n)候选无需扫坐标宽度。这里枚举全部边界更容易完整证明,之后可讨论双指针压到线性处理。
  5. 注意闭区间右端需要+1。若误用r,单个袋[l,l]长度会变0且窗口边界全部错一位。

手动走一遍

训练segments=[(1,3,2),(10,10,9)]、k=3,窗口[8,10]价值9,超过[1,3]的6;不必跨巨大间隙逐坐标枚举。若k足够大可同时包含两段,总15。

Python 参考实现

下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。

代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。

from bisect import bisect_right


def maximum_k_bags(segments,k):
    """Disjoint inclusive integer intervals; nonnegative per-position values."""
    if k <= 0 or any(l>r or v<0 for l,r,v in segments):
        raise ValueError("invalid window or intervals")
    ordered=sorted(segments)
    if any(ordered[i-1][1] >= ordered[i][0] for i in range(1,len(ordered))):
        raise ValueError("overlapping segments")
    starts=[l for l,r,v in ordered]
    prefix=[0]
    endpoints=[]
    for l,r,v in ordered:
        prefix.append(prefix[-1]+(r-l+1)*v)
        endpoints.extend((l,r+1))
    def mass_before(x):
        i=bisect_right(starts,x)-1
        if i < 0: return 0
        l,r,v=ordered[i]
        return prefix[i]+(min(x,r+1)-l)*v
    return max((mass_before(s+k)-mass_before(s)
                for edge in endpoints for s in (edge,edge-k)),default=0)

复杂度

排序O(n log n),4n候选各O(log n)查询,总O(n log n)时间与O(n)空间;与坐标上界无关,数值乘法须检查宽度。

易错点与追问

容易失分的边界

  • 展开10^12坐标。
  • r-l代替r-l+1。
  • 只试段起点,漏掉以段尾对齐的窗口。
  • 重叠段仍用单段F公式。

继续深挖

  • 区间重叠如何先聚合成不重叠常值段?
  • 负value且窗口必须落指定范围怎样改?
  • 双指针如何利用单调候选减少二分?
✓已复制微信号!