题解/算法 {3193. 统计逆序对的数目}
@LINK: https://leetcode.cn/problems/count-the-number-of-inversions/
;
我们令Cont[x] = y
为: A[0...x]
里需要有y
个逆序对 这个要求, y=-1
说明无限制, 注意 题目说明 Cont[N-1] != -1
;
很容易想到是 记忆化DP, 看一个超时做法 Dfs( ind, cont, vector<int> nums) (nums.size()==ind+1)
表示 我们要将nums
放到A[0...,ind]
这些位置上 且这个他需要有cont
个逆序对;
.
举个例子, ind=3, cont=6, nums={0,2,4,6}
, 我们枚举A[ind]
的选择: 放0
则Dfs( ind=2, cont=6-3, nums={2,4,6})
, 放2
则Dfs( ind=2, cont=6-2, nums={0,4,6})
, …
当然 他是超时的, 其实 因为ind == nums.size()-1
, 所以可以把ind
这个状态 给去掉, 即(nums, cont)
其中nums
的种数是C(n,k)
n=300
k=num.size()
, 表示由nums
所组成的所有的(满足条件k
)的排列;
我们其实可以将(nums, cont)
状态 变成 (num.size(), cont)
(其中k==nums.size()
), 换句话说 比如k==4
, 你需要证明 对于任意的nums.size()==k
的nums
其实他们的DP值 都是一样的, 即DP({0,2,4,6}, cont) == DP( {1,3,5,7}, cont) == ... == DP( {0,1,2,3}, cont)
, 因此 对于任意的(nums, cont)
他都可以转换为是( {0,1,2,3}, cont)
;
证明: 对于任意的nums
因为你的DP 关注的是逆序对的个数, 他都可以对应为是{0,1,2,3}
这个集合, 即你关注的只有相对大小;
int numberOfPermutations(int N, vector<vector<int>>& A) {
vector<int> Cont( N, -1);
for( auto & i : A){ Cont[i[0]] = i[1];}
using Mod_ = ___Modulo_<int, int(1e9)+7>;
static int DP[ 302][ 402];
std::memset( DP, -1, sizeof(DP));
auto Dfs = [&]( auto & _dfs, int _ind, int _cont)->int{
if( Cont[_ind]!=-1 && Cont[_ind]!=_cont){ return 0;}
if( _cont<0 || _cont>((1+_ind)*_ind/2)){ return 0;}
if( _ind == 0){
return (_cont == 0 ? 1 : 0);
}
auto & curDP = DP[_ind][_cont];
if( curDP == -1){
Mod_ ANS = 0;
FOR_( ed, 0, _ind){
auto rest = _cont - (_ind-ed);
ANS += _dfs( _dfs, _ind-1, rest);
}
curDP = ANS.Value;
}
return curDP;
};
return Dfs( Dfs, N-1, Cont[N-1]); // 注意`Cont[N-1] != -1`;
}