题解:洛谷 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
【核心思想】
-
问题分析:有 \(N\) 头奶牛(\(N \le 20\)),每头奶牛有高度 \(a_i\);有 \(N\) 个牛棚,每个牛棚有高度限制 \(b_j\),一头奶牛能住进牛棚 \(j\) 当且仅当 \(a_i \le b_j\)。求将 \(N\) 头奶牛分配到 \(N\) 个不同牛棚(每个牛棚恰好一头牛)且满足所有高度限制的方案总数。这是一个带约束的排列计数问题,由于 \(N\) 很小,可以通过排序后按顺序计算每个位置的合法选择数,利用乘法原理得到答案。
-
算法选择:
- 排序 + 贪心计数:将奶牛按高度从大到小排序,然后依次为每头奶牛分配牛棚。对于当前第 \(i\) 头奶牛(从大到小),统计所有高度限制 \(\ge a_i\) 的牛棚总数 \(c\),再减去已经被前 \(i-1\) 头奶牛占用的牛棚数量(\(i-1\)),得到当前奶牛的可选牛棚数。根据乘法原理,将这些可选数相乘即为总方案数。
- 正确性保证:因为奶牛是按高度从大到小处理的,当前奶牛是剩余奶牛中最高(或等高)的,它可选的牛棚集合是剩余牛棚中限制最严格的子集。先处理限制最严格的奶牛,可以确保计数过程中不会出现“当前奶牛选了某个牛棚后,导致后续更矮的奶牛无棚可去”的情况,因为更矮的奶牛能去的牛棚集合是当前奶牛能去集合的超集。
-
关键步骤:
- 输入与排序:读入 \(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。
- 输入与排序:读入 \(N\),奶牛高度数组 \(a[1..N]\),牛棚限制数组 \(b[1..N]\)。将 \(a\) 按从大到小排序(如代码中的
-
时间/空间复杂度:
- 时间复杂度:\(O(N^2)\),因为对每头奶牛都要遍历所有牛棚统计 \(c\)。\(N \le 20\),完全可行。也可优化到 \(O(N \log N)\),但无必要。
- 空间复杂度:\(O(N)\)。
-
排序 + 乘法原理的核心思想:
- 降序处理的必要性:先安排“最挑剔”(最高)的奶牛,确保它只能从有限的牛棚中选择。如果先安排矮的奶牛,可能会错误地占用高的牛棚,导致后续高的奶牛无棚可去,从而漏计合法方案。按高度降序处理是避免重复和遗漏的关键。
- 动态可选数:当处理第 \(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
浙公网安备 33010602011771号