解决方案需要一些解释。让我们用 I(n, k) 来表示 n 项恰好有 k 个反转的排列数
现在 I(n, 0) 始终为 1。对于任何 n 存在一个且只有一个具有 0 个反转的排列,即,当序列越来越排序时
现在 I(0, k) 总是 0 因为我们没有序列本身
现在要找到 I(n, k),让我们以包含 4 个元素 {1,2,3,4} 的序列为例
对于下面的 n = 4 是按反转次数枚举和分组的排列
|___k=0___|___k=1___|___k=2___|___k=3___|___k=4___|___k=5___|___k=6___|
| 1234 | 1243 | 1342 | 1432 | 2431 | 3421 | 4321 |
| | 1324 | 1423 | 2341 | 3241 | 4231 | |
| | 2134 | 2143 | 2413 | 3412 | 4312 | |
| | | 2314 | 3142 | 4132 | | |
| | | 3124 | 3214 | 4213 | | |
| | | | 4123 | | | |
| | | | | | | |
|I(4,0)=1 |I(4,1)=3 |I(4,2)=5 |I(4,3)=6 |I(4,4)=5 |I(4,5)=3 |I(4,6)=1 |
| | | | | | | |
现在要找到 n = 5 的排列数,对于每个可能的 k,我们可以通过在每个排列中的某个位置插入第 n 个(最大)元素(5)从 I(4,k)推导出递归 I(5,k)先前的排列,因此得到的反转次数为 k
例如,I(5,4) 只是序列 {1,2,3,4,5} 的排列数,每个排列恰好有 4 个反转。现在让我们观察上面的 I(4, k) 直到列 k = 4 反转的次数是 <= 4 现在让我们将元素 5 放置如下所示
|___k=0___|___k=1___|___k=2___|___k=3___|___k=4___|___k=5___|___k=6___|
| |5|1234 | 1|5|243 | 13|5|42 | 143|5|2 | 2431|5| | 3421 | 4321 |
| | 1|5|324 | 14|5|23 | 234|5|1 | 3241|5| | 4231 | |
| | 2|5|134 | 21|5|43 | 241|5|3 | 3412|5| | 4312 | |
| | | 23|5|14 | 314|5|4 | 4132|5| | | |
| | | 31|5|24 | 321|5|4 | 4213|5| | | |
| | | | 412|5|3 | | | |
| | | | | | | |
| 1 | 3 | 5 | 6 | 5 | | |
| | | | | | | |
包含 5 的上述每个排列恰好有 4 个反转。所以有 4 个反转的总排列 I(5,4) = I(4,4) + I(4,3) + I(4,2) + I(4,1) + I(4,0) = 1 + 3 + 5 + 6 + 5 = 20
类似地,对于来自 I(4,k) 的 I(5,5)
|___k=0___|___k=1___|___k=2___|___k=3___|___k=4___|___k=5___|___k=6___|
| 1234 | |5|1243 | 1|5|342 | 14|5|32 | 243|5|1 | 3421|5| | 4321 |
| | |5|1324 | 1|5|423 | 23|5|41 | 324|5|1 | 4231|5| | |
| | |5|2134 | 2|5|143 | 24|5|13 | 341|5|2 | 4312|5| | |
| | | 2|5|314 | 31|5|44 | 413|5|2 | | |
| | | 3|5|124 | 32|5|14 | 421|5|3 | | |
| | | | 41|5|23 | | | |
| | | | | | | |
| | 3 | 5 | 6 | 5 | 3 | |
| | | | | | | |
所以 5 次反转的总排列 I(5,5) = I(4,5) + I(4,4) + I(4,3) + I(4,2) + I(4,1) = 3 + 5 + 6 + 5 + 3 = 22
所以I(n, k) = sum of I(n-1, k-i) such that i < n && k-i >= 0
此外,当序列按降序排序时,k 可以上升到 n*(n-1)/2
https://secweb.cs.odu.edu/~zeil/cs361/web/website/Lectures/insertion /pages/ar01s04s01.html
http://www.algorithmist.com/index.php/SPOJ_PERMUT1
#include <stdio.h>
int dp[100][100];
int inversions(int n, int k)
{
if (dp[n][k] != -1) return dp[n][k];
if (k == 0) return dp[n][k] = 1;
if (n == 0) return dp[n][k] = 0;
int j = 0, val = 0;
for (j = 0; j < n && k-j >= 0; j++)
val += inversions(n-1, k-j);
return dp[n][k] = val;
}
int main()
{
int t;
scanf("%d", &t);
while (t--) {
int n, k, i, j;
scanf("%d%d", &n, &k);
for (i = 1; i <= n; i++)
for (j = 0; j <= k; j++)
dp[i][j] = -1;
printf("%d\n", inversions(n, k));
}
return 0;
}