解决方案需要一些解释。我们用I(n, k)表示恰好有k个逆序对的n个项目的排列数。
现在,对于任何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(5, 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(5,5),从I(4,k)得到。
|___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) = I(n-1, k-i)之和,其中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;
}