题解:洛谷 P9942 Just Stalling

【题目来源】

洛谷:P9942 [USACO21JAN] Just Stalling B - 洛谷

【题目描述】

Farmer John 有 \(N\) 头奶牛\((1\le N\le 20)\),高度为\(a_1\dots a_N\)。他的牛栏有 \(N\) 个牛棚,高度限制分别为 \(b_1\dots b_N\)(例如,如果\(b_5=17\),那么一头高度不超过 \(17\) 的奶牛可以住在牛棚 \(5\) 里)。Farmer John 有多少种不同的方式安排他的奶牛,使得每头奶牛均住在不同的牛棚里,并且使得每个牛棚的高度限制均得到满足?

【输入】

输入的第一行包含 \(N\)。第二行包含 \(N\) 个空格分隔的整数 \(a_1,a_2,\dots,a_N\)。第三行包含 \(N\)个空格分隔的整数 \(b_1,b_2,\dots,b_N\)。所有的高度和高度限制均在范围 \([1,10^9]\) 内。

【输出】

输出 Farmer John 可以将每头奶牛安排到不同的牛棚里,使得每个牛棚的高度限制均得到满足的方法数。注意输出的数量可能需要使用 \(64\) 位整数型,例如 C++ 中的 long long。

【输入样例】

4
1 2 3 4
2 4 3 4

【输出样例】

8

【核心思想】

  1. 问题分析:有 \(N\) 头奶牛(\(N \le 20\)),每头奶牛有高度 \(a_i\);有 \(N\) 个牛棚,每个牛棚有高度限制 \(b_j\),一头奶牛能住进牛棚 \(j\) 当且仅当 \(a_i \le b_j\)。求将 \(N\) 头奶牛分配到 \(N\) 个不同牛棚(每个牛棚恰好一头牛)且满足所有高度限制的方案总数。这是一个带约束的排列计数问题,由于 \(N\) 很小,可以通过排序后按顺序计算每个位置的合法选择数,利用乘法原理得到答案。

  2. 算法选择

    • 排序 + 贪心计数:将奶牛按高度从大到小排序,然后依次为每头奶牛分配牛棚。对于当前第 \(i\) 头奶牛(从大到小),统计所有高度限制 \(\ge a_i\) 的牛棚总数 \(c\),再减去已经被前 \(i-1\) 头奶牛占用的牛棚数量(\(i-1\)),得到当前奶牛的可选牛棚数。根据乘法原理,将这些可选数相乘即为总方案数。
    • 正确性保证:因为奶牛是按高度从大到小处理的,当前奶牛是剩余奶牛中最高(或等高)的,它可选的牛棚集合是剩余牛棚中限制最严格的子集。先处理限制最严格的奶牛,可以确保计数过程中不会出现“当前奶牛选了某个牛棚后,导致后续更矮的奶牛无棚可去”的情况,因为更矮的奶牛能去的牛棚集合是当前奶牛能去集合的超集。
  3. 关键步骤

    • 输入与排序:读入 \(N\),奶牛高度数组 \(a[1..N]\),牛棚限制数组 \(b[1..N]\)。将 \(a\)从大到小排序(如代码中的 cmp 函数)。
    • 初始化答案ans = 1(long long 类型)。
    • 枚举每头奶牛\(i = 1\)\(N\)):
      • 统计所有牛棚中,限制 \(b_j \ge a_i\) 的牛棚数量 \(c\)
      • 减去已经被前 \(i-1\) 头奶牛选走的牛棚数:c -= (i - 1)。这些被选走的牛棚一定包含在当前 \(c\) 个可选牛棚中(因为前 \(i-1\) 头奶牛都比当前奶牛高或相等,它们选走的牛棚必然也满足当前奶牛的高度要求)。
      • \(c\) 乘入答案:ans *= c
    • 输出 ans
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N^2)\),因为对每头奶牛都要遍历所有牛棚统计 \(c\)\(N \le 20\),完全可行。也可优化到 \(O(N \log N)\),但无必要。
    • 空间复杂度:\(O(N)\)
  5. 排序 + 乘法原理的核心思想

    • 降序处理的必要性:先安排“最挑剔”(最高)的奶牛,确保它只能从有限的牛棚中选择。如果先安排矮的奶牛,可能会错误地占用高的牛棚,导致后续高的奶牛无棚可去,从而漏计合法方案。按高度降序处理是避免重复和遗漏的关键。
    • 动态可选数:当处理第 \(i\) 头奶牛时,可用的牛棚总数是固定的(满足限制的牛棚数),但其中 \(i-1\) 个已被前面更高的奶牛占据。由于前面更高的奶牛占据的牛棚一定也满足当前奶牛的条件,所以直接从总数中减去 \(i-1\) 即可得到当前奶牛的实际可选数。
    • 乘法原理:每头奶牛的可选牛棚数只取决于之前的选择(但通过降序处理,该数量是确定的,不依赖于之前的具体选择),因此总方案数等于每步可选数的乘积。
    • 适用场景:适用于 \(N\) 较小(\(\le 20\))的排列计数问题,其中每个元素有一组可接受的位置,且可接受性具有偏序关系(如高度限制)。核心是通过排序将复杂依赖转化为顺序独立的乘法。

【算法标签】

普及- #模拟

【代码详解】

#include <bits/stdc++.h>
using namespace std;
int n, a[25], b[25];
bool cmp (int x, int y)  // 定义比较函数,按照从大到小排序
{
    return x>y;
}
int main()
{
    cin >> n;  // 输入n
    for (int i=1; i<=n; i++) cin >> a[i];  // 记录奶牛的高度
    for (int i=1; i<=n; i++) cin >> b[i];  // 记录牛棚的高度
    sort(a+1, a+n+1, cmp);  // 按照牛棚的高度,从大到小排序
    long long ans = 1;  // 定义连乘的初始值为1
    for (int i=1; i<=n; i++) {  // for循环遍历n头奶牛
        int c = 0;  // 计算每头牛可分配的牛棚数量
        for (int j=1; j<=n; j++) {  // 遍历所有牛棚
            if (b[j]>=a[i]) c++;  // 如果牛棚高度大于等于牛的高度,牛棚数量自增1
        }
        c -= (i-1);  // 因为前i-1头牛已经分配了牛棚,所以c要减去(i-1)
        ans *= c;  // 将可用牛棚数量乘以ans,类似于C(1,2)*C(1,2)*C(1,2)*1
    }
    cout << ans << endl;  // 最后输出方案数
    return 0;
}

【运行结果】

4
1 2 3 4
2 4 3 4
8Just Stalling
posted @ 2026-08-29 09:41  团爸讲算法  阅读(7)  评论(0)    收藏  举报