📜  K倒置的排列数|套装2(1)

📅  最后修改于: 2023-12-03 14:43:43.413000             🧑  作者: Mango

K倒置的排列数|套装2

这个题目的主要目的是要求在给定的长度为N的排列中,有多少种情况下倒置的数对个数为K。

输入

输入一共包含两行。第1行是两个整数N,K。第2行包含N个不同的整数,代表长度为N的排列。所有的整数均在1到N之间(含1和N)。

5 2
5 3 1 4 2
输出

输出一个整数,表示倒置的数对个数为K的排列有多少种。输出对10^9+7取模后的结果。

6
解题思路

首先,对于一个长度为N的排列,真正的倒置数对最多只有 N*(N-1)/2 个,因为两个数不会重复计算。

假设有一个字符集合S, 我们可以考虑每一位我们填什么字符是合法的, 那么合法排列的数目应该是|S^n|, 现在的问题是怎么算这个S。

考虑选了一位,那么它最后被统计到的逆序对数量就是 n - 1,因为此时只剩下 n-1 个数。

如果我们要求一个排列中逆序对数量为k,我们就可以枚举每一个数字。

如果我们把这个数字固定,那么对于选择了这个数的逆序对来说,一定恰好出现在剩下的 n-1 个数中。

那么对于剩下的 n-1 个数,我们需要求的逆序对数量是 k-i(其中i表示当前数字在剩下的n-1个数中排名)。

换句话说,我们要在 n-1 个数中选出 k-i 个数,放到数字 i 的后面,这样就可以让数字 i 的贡献变成 k 了。

因此,我们可以列出动态转移方程:

$$ dp[i][j] = \sum_{k=0} ^{i-1}dp[k][j - (i-k)] $$

这里 dp[i][j] 表示前i个数,逆序对数量为j的排列数目。

动态转移方程的意义是,我们枚举第i个数所在的位置,然后从前i-1个数中选出 j-(i-k)个数放到第i个数的后面。累加这个方案数即可。

注意到如果j-i*(i-1)/2<0,那么逆序对数量明显是不够的,需要跳过这个状态,初始化dp[0][0]为1即可。

代码实现
#coding=utf-8
import sys

n,m=map(int,input().split())

arr = list(map(int, input().split()))
arr = [0] + arr

mod = 1000000007

f = [[0] * (n + 1) for j in range(n + 1)]
for i in range(1, n+1):
    f[0][i] = 1

ans = 0
for i in range(1, n+1):
    for j in range(1, i + 1):
        f[i][j] = (f[i - 1][j - 1] + f[i - 1][j] * (i - 1)) % mod
  
ans = f[n-1][m]
print(ans)
收获

通过做这个题目,我深刻理解了 逆序对 和 状压dp 的理念,也学会了如何推导和编写状压dp的动态转移方程。同时,我也加深了对 “组合数学” 中一些知识点的理解和应用能力。

此外,我也学习到了一些Python的语法。例如列表推导、嵌套列表等等。这个过程也让我增强了对Python语言的熟悉度和掌握程度。