算法 {排列,排列数}
排列
定義
#標準排列#
序列A[n]
裏 所有元素為{1,2,3,...,n}
;
.
比如[3,1,4,2]
是一個排列;
@DELI;
#(一般)排列#
序列A[n]
裏 所有元素均不同;
.
比如[33,11,44,22]
是一個排列 (因爲他可以離散化為[3,1,4,2]
成爲標準排列);
算法
将任意排列等价于标准排列{0,1,2,3...}
, 即将C(n,k)
优化为O(1)
比如 你的DP定义是DP( vector<int> nums, m)
, 其中nums = 从[0,n)中任意选择k个不同的数 所组成的集合
, DP表示所有nums
所表示的排列中 满足某种要求m
的合法排列 (比如nums.size()=3, n=5
那么nums
可以等于{0,1,2}, {0,1,3}, {0,1,4}, {0,2,3}, ...
, 比如nums={0,2,4}
那么他可以表示[024, 042, 204, ...]
这些排列);
他的时间复杂度是 C(n,k) * m
, 而假如说 你可以证明 不管nums
等于多少 只要nums.size()
不变 那么他们的DP值是一样的, 即DP( {0,1,2}, m) == DP({0,1,3}, m) == DP({2,3,4}, m) == DP({0,1,4}, m)
, 那么 你可以将DP 优化为DP( k, m)
其中k == nums.size()
这是因为 对于C(n,k)
的任意方案 他们的DP值都一样 因此任意的DP( nums, m) == DP( {0,1,2}, m) (假设nums.size()==3)
, 因此 之前nums
的种类是C(n,k)
现在种类是1
个 即{0,1,2,3...}
这个连续递增集合;
例题: @LINK: https://editor.csdn.net/md/?not_checkout=1&articleId=140069535
;
性质
#交換排列中的兩個不同元素, 逆序對個數會產生奇偶性變化#
任意一個排列...A[i]...A[j]...
假設此時逆序對個數為X0
, 令排列...A[j]...A[i]...
的逆序對個數為X1
, 那麽X0,X1
的奇偶性是不同的;
.
比如排列[12345]
交换两个元素后[14325]
, 原逆序对个数为0
现在逆序对为3
, 奇偶性不同;
.
注意, 這裏講的是排列 (即所有元素都不同), 而不是序列 , 比如序列[322]
有2
逆序對 交換后變成[223]
有0
個 奇偶相同;
@DELI;
#证明#
若原逆序对个数为
a
a
a, 交换后的个数为
b
b
b, 我们要证明:
a
+
b
a+b
a+b为奇数;
一个排列: ... [A] [mid] [B] ...
, 将其所有逆序对分为2类:
1 一个逆序对(x,y)
, [x = A
, 则y = mid/B
]–OR–[y = B
, 则x = A/mid
];
.
换句话说, 当我们swap(A,B)
, 这一类的逆序对 不再是逆序对了;
2 否则, 为此类逆序对, 假设有X
个;
记[mid]
的长度为L
, 且mid
中有a_less
个
<
A
< A
<A的 (即有L - a_less
个
>
A
> A
>A的), 有b_greater
个
>
B
> B
>B的 (即有L - b_greater
个
<
B
< B
<B的);
令
f
=
1
f=1
f=1如果
A
>
B
A>B
A>B, 否则
f
=
0
f = 0
f=0;
未交换前, 逆序对个数为: X + a_less + b_greater + f
;
交换之后的逆序对个数为: X + (L - a_less) + (L - b_greater) + (1 - f)
;
.
他俩的和, 是奇数;
@DELI;
排列数
算法
模數為質數|查詢O(logN)
|預處理O(N)
@LINK: (https://editor.csdn.net/md/?not_checkout=1&articleId=134768975)-(@LOC_0)
;
模數為質數|查詢O(1)
|預處理O(N*logN)
@LINK: (https://editor.csdn.net/md/?not_checkout=1&articleId=134768975)-(@LOC_1)
;
直接暴力
template< class _RetType_, class _T> _RetType_ ___GetPermutation_brutal( _T _n, _T _m){
if( false == (_n>=_m && _m>=0)){ return 0;}
_RetType_ ANS = 1;
for( auto i = _m-1; i >= 0; --i){ ANS *= (_n - i);}
return ANS;
} // ___GetPermutation_brutal
@DELI;
可以(非質數)取模, 也可以不取模
例题
汇总
@LINK: https://editor.csdn.net/md/?not_checkout=1&articleId=133279832
从树中除掉若干点后 树变成了若干个连通块;
@DELI;
LINK: https://editor.csdn.net/md/?articleId=129215215
;
@DELI;
一个
m
∗
n
m * n
m∗n的方格, 放入
k
≤
m
i
n
(
m
,
n
)
k \leq min(m,n)
k≤min(m,n)个小球, 使得同一行和同一列内 最多有
1
1
1个小球;
方案数为
C
m
k
∗
C
n
k
∗
k
!
C_m^k * C_n^k * k!
Cmk∗Cnk∗k!;
.
C
m
k
∗
C
n
k
C_m^k * C_n^k
Cmk∗Cnk相当于是选了一个
k
∗
k
k*k
k∗k的正方形, 然后放
k
k
k个小球; 令
(
c
1
,
c
2
,
.
.
.
,
c
k
)
(c_1, c_2, ..., c_k)
(c1,c2,...,ck) 其中
c
i
c_i
ci表示第
i
i
i行的小球 所处的列号; 显然, 有
k
∗
(
k
−
1
)
∗
.
.
.
∗
1
k * (k-1) * ... * 1
k∗(k−1)∗...∗1种方案;
.
该式可以继续化简为:
C
m
k
∗
P
m
k
C_m^k * P_m^k
Cmk∗Pmk;
@Delimiter
錯誤
#並不是所有的排列組合問題 他的答案 都對應著一個公式 (比如
C
n
k
∗
(
n
−
1
)
k
C_n^k * (n-1)^k
Cnk∗(n−1)k 然後把你的參數 直接套進這個公式裡 就得到答案了)#
有些情況 你得不到這樣的 直接公式, 只能通過一步步的 遞推來進行; @LINK: @LOC_3