给你 n 笔订单,每笔订单都需要快递服务。
请你统计所有有效的 收件/配送 序列的数目,确保第 i 个物品的配送服务 delivery(i) 总是在其收件服务 pickup(i) 之后。
由于答案可能很大,请返回答案对 10^9 + 7 取余的结果。
示例 1:
输入:n = 1
输出:1
解释:只有一种序列 (P1, D1),物品 1 的配送服务(D1)在物品 1 的收件服务(P1)后。
示例 2:
输入:n = 2
输出:6
解释:所有可能的序列包括:
(P1,P2,D1,D2),(P1,P2,D2,D1),(P1,D1,P2,D2),(P2,P1,D1,D2),(P2,P1,D2,D1) 和 (P2,D2,P1,D1)。
(P1,D2,P2,D1) 是一个无效的序列,因为物品 2 的收件服务(P2)不应在物品 2 的配送服务(D2)之后。
示例 3:
输入:n = 3
输出:90
提示:
1 <= n <= 500
方法一:
找规律。
首先把P1~Pn做好排列,共有n! 种排列方法。
然后拿出其中一例,讨论Dn的排列
P1,P2,P3,… , Pn
首先看Dn,它只有一处能放,它需放在Pn后面,得到P1,… ,Pn-1,Pn,Dn
然后Dn-1,它能放在Pn前面,Pn后面Dn前面,Dn后面,共3种
再看Dn-2,可验证有5种
后面就不一一验证了,一共1,3,5,… ,2i-1种
上面只是一种P的排列,那P一共n!种排列方式,
所以P和D结合起来一共n!* (1 * 3 * 5 * … * (2n-1))种
注意结果要用long型,因为在取模之前有可能已经溢出
参考链接
public int countOrders(int n) {
long result = 1;
int mod = 1000000007;
for(int i = 1; i <= n; i++) {
result *= i; //n!
result %= mod;
result *= (2*i - 1);
result %= mod;
}
return (int)(result %= mod);
}
方法二:
DP
代码和上面一样,只是思维方式不一样
令DP[i]表示有i个点时有多少种排列
如果有i-1个物品,那么因为同时有P和D,所以一共会有2(i-1)个点
现在看第i个物品,会引入Pi和Di
Pi引入后,如果Pi放在index=0处,那么Di可放的位置有2i-1处(index=0后面有2i-2个点,它们加上左右两端的缝隙有2i-2+1处缝隙可放)
如果Pi放在index=1处,Di可放的位置有2i-2处
…
如果Pi放在最后,Di也只能放在最后1处
一共有1+2+…+(2i-1) = i * (2i-1)
再来一个Pi+1的话,就会在此基础上再乘(i+1) * (2I+1)
所以dp[i] = dp[i-1] * i * (2i-1)
代码和上面一样
参考链接