Skip to content

课堂计划

## USACO 2021 US Open, Silver Problem 3. Acowdemia

https://usaco.org/index.php?page=viewproblem2&cpid=1136

我们需要找到最大的 h 指数,即至少有 h 篇论文,每篇至少有 h 次引用。Bessie 可以通过调查文章增加引用次数,每篇调查文章最多引用 L 篇论文,总共可以写 K 篇调查文章。

首先,将论文的引用次数降序排序,这样可以优先处理引用次数高的论文,减少需要增加的引用次数。

然后使用二分查找来确定最大可能的 h 值。我们在 0 到 n 之间查找,通过中间值来验证是否能达到某个 h 指数。

检查函数 ok(h) 用于验证是否能通过增加引用次数达到某个 h 指数。它统计当前已经有至少 h 次引用的论文,对于不足 h 次引用的论文,计算需要补充的引用次数,并检查总的可用引用次数是否足够。

这种方法通过二分查找高效地逼近最大可能的 h,并确保找到了在给定约束下的最大 h 指数。

n, k, L = map(int, input().split())

# 读取每篇论文的引用次数,并按降序排序
c = sorted(list(map(int, input().split())), reverse=True)

# 定义一个函数来检查是否可以达到给定的 h 指数
def ok(h):
    cnt = 0  # 记录至少有 h 次引用的论文数量
    tot = k * L  # 可以分配的总额外引用次数(k 篇调查,每篇 L 次引用)

    # 遍历每篇论文的引用次数
    for x in c:
        if x >= h:
            cnt += 1  # 如果该论文引用次数已经至少是 h,计入满足条件的论文数
            continue
        # 计算该论文需要的额外引用次数以达到 h 次引用
        if h - x <= tot and h - x <= k:
            tot -= h - x  # 使用额外的引用次数
            cnt += 1  # 计入满足条件的论文数

    # 返回是否有至少 h 篇论文满足条件
    return cnt >= h

# 二分查找来确定最大可能的 h 指数
l, r, ans = 0, n, -1
while l <= r:
    mid = (l + r) // 2
    if ok(mid):
        ans = mid  # 如果当前的 h 可行,尝试更大的 h
        l = mid + 1
    else:
        r = mid - 1  # 如果当前的 h 不可行,尝试更小的 h

# 输出最大可能的 h 指数
print(ans)

程序填空:

n, k, L = map(int, input().split())

# 读取每篇论文的引用次数,并按降序排序
c = ___

# 定义一个函数来检查是否可以达到给定的 h 指数
def ok(h):
    cnt = 0  # 记录至少有 h 次引用的论文数量
    tot = ___  # 可以分配的总额外引用次数(k 篇调查,每篇 L 次引用)

    # 遍历每篇论文的引用次数
    for x in c:
        if x >= h:
            ___  # 如果该论文引用次数已经至少是 h,计入满足条件的论文数
            continue
        # 计算该论文需要的额外引用次数以达到 h 次引用
        if h - x <= tot and h - x <= k:
            tot -= ___  # 使用额外的引用次数
            cnt += 1  # 计入满足条件的论文数

    # 返回是否有至少 h 篇论文满足条件
    return cnt >= h

# 二分查找来确定最大可能的 h 指数
l, r, ans = 0, n, -1
while l <= r:
    mid = ___
    if ___:
        ans = mid  # 如果当前的 h 可行,尝试更大的 h
        ___
    else:
        r = mid - 1  # 如果当前的 h 不可行,尝试更小的 h

# 输出最大可能的 h 指数
print(ans)

P6282 [USACO20OPEN] Cereal S

https://usaco.org/index.php?page=viewproblem2&cpid=1039

如果我们增加奶牛,即倒着求每个答案,最后倒着输出就行了。

如果增加一头奶牛,又会出现什么情况呢?

  • 如果它最喜欢的麦片没有被选择,那么它就会选它,且不影响其他奶牛;

  • 如果它最喜欢的麦片被选择,因为它的优先级比其他牛都高,所以会把其他牛的麦片“抢”过来,其他牛只能“退而求其次”。

递归求解即可,因为所有奶牛最多选两次,所以 solvesolve 函数的复杂度是 Θ(1)Θ(1) 的,整个代码的时间复杂度为 Θ(N)Θ(N)。

实现方法见代码注释:

cici​ 表示第 ii 头奶牛的喜好,hihi​ 表示拿走第 ii 种麦片的牛的编号,resiresi​ 表示移走 i−1i−1 头牛的答案,curcur 是计算答案的计数器。

因为领麦片的牛越多,领到麦片的牛就越多,所以越往后面,curcur 就越大,故每次计算的时候 curcur 不需要清零。

N = 123456

class Cow:
    def __init__(self, f, s):
        self.f = f
        self.s = s

c = [Cow(0, 0) for _ in range(N)]
h = [0] * N
res = [0] * N
cur = 0

def solve(x, y):
    global cur
    if h[y] == 0:
        h[y] = x
        cur += 1
    elif h[y] > x:
        z = h[y]
        h[y] = x
        if y == c[z].f:
            solve(z, c[z].s)

def main():
    global cur
    n, m = map(int, input().split())
    for i in range(1, n + 1):
        f, s = map(int, input().split())
        c[i] = Cow(f, s)

    for i in range(n - 1, -1, -1):
        solve(i + 1, c[i + 1].f)
        res[i + 1] = cur

    for i in range(1, n + 1):
        print(res[i])

if __name__ == "__main__":
    main()

作业题 # P2997 [USACO10NOV] Banner S

https://www.luogu.com.cn/problem/P2997

平面上有(0,0)到(n,m)的(n+1)*(m+1)个点。问有多少点对所连的线段不过其他点,且长度在[l,h]范围内