【碎碎念:我终于看懂并且理解3193这道题的代码了,我滴天了,不愧是“困难”的题,看灵神的解析我都看了好几遍才懂,中间实在是烦了,我就停了几天又重新看,才理解代码为啥要那样写(主要是那个j的范围啥的没看懂,大体框架还是很好理解)】
3193. 统计逆序对的数目
(个人觉得,此题看英文挺好理解些)
3193. 统计逆序对的数目
学习视频
灵茶山艾府:3193.统计逆序对的数目详细讲解
讲解之神!!全靠讲解才能理解啊!太感谢灵神了!!!/(ㄒoㄒ)/~~
学习笔记
- 逆序对【之前从来没听过,第一次看题,一脸懵】:整数数组nums满足i<j并且nums[i]>nums[j] (i,j为nums的index)则称为逆序对
# 用暴力的方法找逆序对的代码
count = 0
# ①正向查找nums
for i in range(len(nums)-1):
for j in range(i+1,len(nums)-1):
if nums[i] > nums[j]:
count += 1
# ②逆向查找nums,就是统计index为j的左边有多少个大于nums【j】的个数
for j in range(len(nums)-1, 0, -1):
for i in range(j):
if nums[i] > nums[j]:
count += 1
-
确定状态定义和状态转移方程
【分析】
假设n = 4,即对perm = 【0,1,2,3】进行排序,j = 5(假设perm可以排序产生5个逆序对)
现在有四个空位置【①】,【②】,【③】,【④】,(用上面的逆向思维) 假设④号位上为数字3,那么也就是说产生了 k=0个逆序对,间接说明前面 ① 号位 到 ③ 号位需要产生 j - k = 5个逆序对。
由此得到dfs(i, j) 表示构造排列perm[0] ~ perm[i] 能构造出多少个逆序对 = j的排列
状态转移方程:dfs(i, j) += dfs(i-1 , j-k )
i-1:往前移一个位置,对应上面就是④号位结束之后,开始考虑③号位的情况
j-k:① 号位 到 ③ 号位需要产生的逆序对 -
r的范围
一直纠结这道题也是因为代码中r<=j<=i+r始终没有理解,后来耐下心捋一遍就懂了
j<r: 因为j一直就是减少的状态,所以如果 j 比要求的 逆序对数量 r 还要小,无法满足条件,故此时的dfs(i,j) = 0
j - i > r : 极端的想,在最后一个位置放最小的值,形成的逆序对为i个,如果剩余的逆序对数量 j 减去剩余元素排列后最大逆序对数量 还是大于规定的r那么此时的dfs(i,j) = 0
【题解】
class Solution:
def numberOfPermutations(self, n: int, requirements: List[List[int]]) -> int:
MOD = 1_000_000_007
req = [-1] * n
req[0] = 0
for end,cnt in requirements:
req[end] = cnt
if req[0]:
return 0
@cache
def dfs(i,j):
if i==0:
return 1
r = req[i-1]
# 如果对前面排序的逆序对数量有要求
if r>=0:
# j < r 因为j一定是不断变小的,如果一开始j就小于规定的r那么就return 0
# j - i > r 在最后一个位置放最小的值,形成的逆序对为i个,如果剩余的逆序对数量j减去
# 剩余元素排序后最大逆序对数量 还是大于规定的r那么也要return 0
return dfs(i-1, r) if r<=j<=i+r else 0
return sum(dfs(i-1, j-k) for k in range(min(i,j)+1)) % MOD
return dfs(n-1, req[-1])