题目链接
题目描述
公园内有 条东西向小径与
条南北向小径,彼此相交形成
个交叉点。现有
张长椅需要放置,要求:
- 每张长椅放在某个交叉点;
- 同一条小径上 至多 放置一张长椅。
请计算满足条件的放置方案数量。输入 满足
。
解题思路
这个问题是在一个 的网格中放置
张长椅,并要求任意两张长椅都不能在同一行或同一列。这是一个经典的组合计数问题。
我们可以分两步来构造一个合法的放置方案:
第一步:选择放置长椅的行和列
要放置 张长椅,它们必须占据
个不同的行和
个不同的列。
- 选择 5 个不同的行:从
个可用的行中,选择
个来放置长椅。这是一个组合问题,方案数为
。
- 选择 5 个不同的列:同样,从
个可用的列中,选择
个。方案数也是
。
第二步:将选定的行和列进行配对
现在我们已经选定了 个行和
个列,形成了一个
的子网格。我们需要在这个子网格中放置
张长椅,且每行每列恰好一个。
- 对于第一个选定的行,我们可以在
个选定的列中任选一个位置。
- 对于第二个选定的行,我们只剩下
个可选的列。
- ...
- 对于最后一个选定的行,只剩下
个列可选。
因此,配对的方案数是 。
最终公式
根据乘法原理,总方案数是以上所有步骤方案数的乘积:
另一种等价思路
我们也可以这样思考:
- 选择 5 个行:有
种方法。
- 为这 5 行分配不同的列:
- 对于第一个被选中的行,我们有
个列可以选择。
- 对于第二个被选中的行,我们有
个列可以选择。
- ...
- 对于第五个被选中的行,我们有
个列可以选择。 这部分的方案数是一个排列数
。
- 对于第一个被选中的行,我们有
总方案数为这两个步骤的乘积:
这两个公式是等价的,因为 。
实现
由于 最大为
,结果会很大,需要使用 64 位整型(
long long
)来存储。我们可以先计算 ,然后计算
,最后将两者相乘。
代码
#include <iostream>
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int n;
cin >> n;
// 计算 P(n, 5)
long long p_n_5 = 1;
for (int i = 0; i < 5; ++i) {
p_n_5 *= (n - i);
}
// 计算 C(n, 5)
long long c_n_5 = p_n_5 / 120;
// 总方案数 = C(n, 5) * P(n, 5)
long long ans = c_n_5 * p_n_5;
cout << ans << endl;
return 0;
}
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
// 计算 P(n, 5)
long p_n_5 = 1;
for (int i = 0; i < 5; i++) {
p_n_5 *= (n - i);
}
// 计算 C(n, 5)
long c_n_5 = p_n_5 / 120;
// 总方案数 = C(n, 5) * P(n, 5)
long ans = c_n_5 * p_n_5;
System.out.println(ans);
}
}
import math
n = int(input())
# 计算 P(n, 5)
p_n_5 = math.perm(n, 5)
# 计算 C(n, 5)
c_n_5 = math.comb(n, 5)
# 总方案数 = C(n, 5) * P(n, 5)
ans = c_n_5 * p_n_5
print(ans)
算法及复杂度
-
算法:组合学、排列组合计数
-
时间复杂度:由于放置的长椅数量是固定的 5,计算
和
的循环次数是常数。因此,时间复杂度为
。
-
空间复杂度:只使用了常数个变量来存储结果,空间复杂度为
。