[USACO11NOV] Cow Lineup S

GitHub跳转原题关系图返回列表

按坐标排序奶牛,用 Counter 和双指针维护包含全部品种的最短坐标窗口。

OJ: luogu

题目 ID: P3029

难度:普及/提高-

标签:双指针滑动窗口计数python

日期: 2026-07-16 17:48

题意

在数轴上选择一个最短区间,使其中至少包含全体奶牛中每一种品种。

思路

先按坐标排序。右指针逐个加入奶牛;窗口已经包含全部品种时,不断移动左指针并更新长度,直到刚好缺少一种品种。

Python 知识

  • Counter 保存窗口内各品种次数,删除计数归零的键后,len(counts) 就是当前品种数。
  • 集合推导式 {breed for _, breed in cows} 统计全局不同品种。
  • 元组排序同时保留坐标和品种信息,参见 /home/rainboy/mycode/hugo-blog/content/program_language/python/collections_toolkit.md

代码

python

from functools import partial
import sys
from itertools import batched
from collections import Counter
from bisect import bisect_left, bisect_right

data = list(map(int,sys.stdin.buffer.read().split()))
n = data[0]

cnt = Counter()
ans = 2 ** 30


def flow(value, *steps):
    for step in steps:
        value = step(value)
    return value


# cows = list(batched(data[1:],2))
# cows.sort(key=lambda x: x[0])

cows = flow(
        batched(data[1:],2),
        list,
        partial(sorted,key= lambda x : x[0])
        )

# 离散化
dif = flow(
    {id for _ , id in cows},
    list,
    sorted,
    )

nid = lambda id: bisect_left(dif,id)
cows_difed = [(h,nid(id)) for h,id in cows]

# 不同的品种的奶牛的数量
required = len(dif)
# print(dif)
# print(cows_difed)

# 双指针
left = 0

for right in range(len(cows_difed)):
    (pos,id) = cows_difed[right]
    cnt[id] += 1
    while len(cnt) == required:
        ans = min(ans,pos - cows_difed[left][0])
        lid = cows_difed[left][1]
        cnt[lid] -= 1
        if not cnt[lid]:
            del cnt[lid]
        left+=1

print(ans)

复杂度

时间复杂度 O(nlogn)O(n\log n),空间复杂度 O(n)O(n)

总结

“最短区间覆盖全部类别”是排序后滑动窗口的典型模型。