给定一个数组,反转被定义为一对 a[i], a[j] 使得 a[i] > a[j] 和 i < j。给定两个数 N 和 k,我们需要知道第一个 N 数有多少个排列正好有 K 反转。
例子:
Input : N = 3, K = 1
Output : 2
Explanation :
Total Permutation of first N number,
123, 132, 213, 231, 312, 321
Permutation with 1 inversion : 132 and 213
Input : N = 4, K = 2
Output : 5
解决这个问题的一个天真的方法是记下所有排列,然后检查它们中的反转计数,但遍历排列本身将花费 O(N!) 时间,这太长了。
我们可以使用动态规划方法解决这个问题。下面是递归公式。
If N is 0, Count(0, K) = 0
If K is 0, Count(N, 0) = 1 (Only sorted array)
In general case,
If we have N number and require K inversion,
Count(N, K) = Count(N - 1, K) +
Count(N – 1, K - 1) +
Count(N – 1, K – 2) +
.... +
Count(N – 1, 0)
上面的递归公式是如何工作的?
如果我们有 N 个数,想要有 K 个排列,假设 (N – 1) 个数的所有排列都写在某处,那么新数(第 N 个和最大的数)需要放在 (N – 1) 个数的所有排列中,并且那些在添加这个数字后反转计数变为 K 的那些应该添加到我们的答案中。现在取那些让 (K – 3) 倒置的 (N – 1) 数的排列集合,现在我们可以将这个新的最大数从最后一个位置放在第 3 位,那么倒置计数将为 K,因此 count(N – 1 , K – 3) 应该添加到我们的答案中,对于其他反演也可以给出相同的论点,我们将达到上述递归作为最终答案。
下面的代码是按照上面的递归记忆的方式编写的。
C++
// C++ program to find number of permutation
// with K inversion using Memoization
#include
using namespace std;
// Limit on N and K
const int M = 100;
// 2D array memo for stopping
// solving same problem again
int memo[M][M];
// method recursively calculates
// permutation with K inversion
int numberOfPermWithKInversion(int N, int K)
{
// base cases
if (N == 0)
return 0;
if (K == 0)
return 1;
// if already solved then
// return result directly
if (memo[N][K] != 0)
return memo[N][K];
// calling recursively all subproblem
// of permutation size N - 1
int sum = 0;
for (int i = 0; i <= K; i++)
{
// Call recursively only
// if total inversion
// to be made are less
// than size
if (i <= N - 1)
sum += numberOfPermWithKInversion(N - 1,
K - i);
}
// store result into memo
memo[N][K] = sum;
return sum;
}
// Driver code
int main()
{
int N = 4;
int K = 2;
cout << numberOfPermWithKInversion(N, K);
return 0;
}
Java
// Java program to find number of permutation with
// K inversion using Memoization
import java.io.*;
class GFG {
// Limit on N and K
static int M = 100;
// 2D array memo for stopping solving same problem
// again
static int memo[][] = new int[M][M];
// method recursively calculates permutation with
// K inversion
static int numberOfPermWithKInversion(int N, int K)
{
// base cases
if (N == 0)
return 0;
if (K == 0)
return 1;
// if already solved then return result directly
if (memo[N][K] != 0)
return memo[N][K];
// calling recursively all subproblem of
// permutation size N - 1
int sum = 0;
for (int i = 0; i <= K; i++) {
// Call recursively only if total inversion
// to be made are less than size
if (i <= N - 1)
sum += numberOfPermWithKInversion(N - 1,
K - i);
}
// store result into memo
memo[N][K] = sum;
return sum;
}
// Driver code to test above methods
public static void main(String[] args)
{
int N = 4;
int K = 2;
System.out.println(numberOfPermWithKInversion(N, K));
}
}
// This code is contributed by vt_m.
Python3
# Python3 program to find number of permutation
# with K inversion using Memoization
# Limit on N and K
M = 100
# 2D array memo for stopping
# solving same problem again
memo = [[0 for i in range(M)] for j in range(M)]
# method recursively calculates
# permutation with K inversion
def numberOfPermWithKInversion(N, K):
# Base cases
if (N == 0): return 0
if (K == 0): return 1
# If already solved then
# return result directly
if (memo[N][K] != 0):
return memo[N][K]
# Calling recursively all subproblem
# of permutation size N - 1
sum = 0
for i in range(K + 1):
# Call recursively only if
# total inversion to be made
# are less than size
if (i <= N - 1):
sum += numberOfPermWithKInversion(N - 1, K - i)
# store result into memo
memo[N][K] = sum
return sum
# Driver code
N = 4; K = 2
print(numberOfPermWithKInversion(N, K))
# This code is contributed by Anant Agarwal.
C#
// C# program to find number of
// permutation with K inversion
// using Memoization
using System;
class GFG
{
// Limit on N and K
static int M = 100;
// 2D array memo for stopping
// solving same problem again
static int [,]memo = new int[M, M];
// method recursively calculates
// permutation with K inversion
static int numberOfPermWithKInversion(int N,
int K)
{
// base cases
if (N == 0)
return 0;
if (K == 0)
return 1;
// if already solved then
// return result directly
if (memo[N, K] != 0)
return memo[N, K];
// calling recursively all
// subproblem of permutation
// size N - 1
int sum = 0;
for (int i = 0; i <= K; i++)
{
// Call recursively only if
// total inversion to be
// made are less than size
if (i <= N - 1)
sum += numberOfPermWithKInversion(N - 1,
K - i);
}
// store result into memo
memo[N, K] = sum;
return sum;
}
// Driver Code
static public void Main ()
{
int N = 4;
int K = 2;
Console.WriteLine(numberOfPermWithKInversion(N, K));
}
}
// This code is contributed by ajit
PHP
Javascript
C++
// C++ program to find number of permutation
// with K inversions
#include
using namespace std;
int numberOfPermWithKInversions(int N, int K) {
vector> dp(N+1,vector(K+1));
// As for k=0, number of permutations is 1 for every N
for(int i = 1; i <= N; i++)
dp[i][0] = 1;
// Using Dynamic Programming with cumulative sum
for(int i = 1; i <= N; i++)
{
for(int j = 1; j <= K; j++)
{
// This is same as val = dp[i-1][j] - dp[i-1][j-i]
// i.e. dp[i-1][j........j-i], just taking care of
// boundaries
int val = dp[i-1][j];
if(j >= i)
val -= dp[i-1][j-i];
dp[i][j] = dp[i][j-1] + val;
}
}
// And, in the end calculate the dp[n][k]
// which is dp[n][k]-dp[n][k-1]
int ans = dp[N][K];
if(K >= 1)
ans -= dp[N][K-1];
return ans;
}
int main() {
int N = 4;
int K = 2;
cout << numberOfPermWithKInversions(N,K) << "\n";
return 0;
}
输出
5
时间复杂度: O(N*N*K)
优化方法:使用制表和累积总和
C++
// C++ program to find number of permutation
// with K inversions
#include
using namespace std;
int numberOfPermWithKInversions(int N, int K) {
vector> dp(N+1,vector(K+1));
// As for k=0, number of permutations is 1 for every N
for(int i = 1; i <= N; i++)
dp[i][0] = 1;
// Using Dynamic Programming with cumulative sum
for(int i = 1; i <= N; i++)
{
for(int j = 1; j <= K; j++)
{
// This is same as val = dp[i-1][j] - dp[i-1][j-i]
// i.e. dp[i-1][j........j-i], just taking care of
// boundaries
int val = dp[i-1][j];
if(j >= i)
val -= dp[i-1][j-i];
dp[i][j] = dp[i][j-1] + val;
}
}
// And, in the end calculate the dp[n][k]
// which is dp[n][k]-dp[n][k-1]
int ans = dp[N][K];
if(K >= 1)
ans -= dp[N][K-1];
return ans;
}
int main() {
int N = 4;
int K = 2;
cout << numberOfPermWithKInversions(N,K) << "\n";
return 0;
}
输出
5
时间复杂度: O(N*K)
如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程和学生竞争性编程现场课程。