Here is a summary of the problem:
#1: n = 3, k = 0. The answer is 1 -- only the permutation (1,2,3) has no inverse pairs. #2: n = 3, k = 1. The answer is 2. The permutations (1,3,2) and (2,1,3) have exactly one inverse pair.
The program is in permutation.cpp and I assume you can write it quickly -- use the C++ routine next_permutation(). It gets slow when n gets to 10.
UNIX> g++ permutation.cpp UNIX> echo 7 7 | ./a.out 359 UNIX> echo 8 8 | ./a.out 1415 UNIX> echo 9 9 | ./a.out 5545 UNIX> echo 10 10 | ./a.out # This one takes over a second 21670 UNIX>
Let's take example 2 as an example. The vector is 0, 1, 2. If I don't move the zero, then the answer is the same as when n=2 and k=1: It is one.
Now, suppose we move the 0 to index 1, so that the vector is 1, 0, 2, ..., n-1. Now there is one inverse pair -- 0 and 1, and so long as the 0 is in index 1, it doesn't matter what we do with the rest of the elements -- the 0 will be part of exactly one inverse pair -- with whatever element is in index 0. So let's delete the 0. The answer is going to be the same as when n is n-1 and k is k-1.
Again, let's take example 2 as an example: the vector is 0, 1, 2. If we move the 0 to index 1, so that the vector is 1, 0, 2, then the number of permutations is the same as when n is 2, and k is zero: It is one.
Continuing, suppose we move the 0 to index 2, so that the vector is 1, 2, 0, 3, ..., n-1. Now the answer is the same as when n is n-1 and k is k-2.
See the pattern? Let's call the problem knp(n,k). You can prove to yourself that the answer is:
The sum of knp(n-1,k-i) for i going from 0 to min(k,n-1). |
Go ahead and program this up -- it's pretty straightforward. While you're at it, add a memoization cache on n and k. I have this solution in too-slow.cpp. We can run it on the same examples as we did with permutation.cppbefore:
UNIX> g++ too-slow.cpp UNIX> echo 7 7 | ./a.out 359 UNIX> echo 8 8 | ./a.out 1415 UNIX> echo 9 9 | ./a.out 5545 UNIX> echo 10 10 | ./a.out 21670 UNIX>It's much faster, too. However, before you submit that solution to Leetcode, you should test the hardest values of n and k:
UNIX> time echo 1000 1000 | ./a.out 663677020 real 0m5.343s user 0m5.331s sys 0m0.007s UNIX>Crap. Glad I did that before submitting, but now we have to think harder.
So, let's take a look at the recursive calls and see if we can spot a better recursion. Let's examine what we do when we call knp(4,12). As it turns out, we make the following calls:
knp(3,12) knp(3,11) knp(3,10) knp(3,9) |
Now, what recursive calls do we make when we call knp(4,11). It's the following:
knp(3,11) knp(3,10) knp(3,9) knp(3,8) |
This gives us a new recursion: knp(4,12) = knp(4,11) + knp(3,12) - knp(3,8). Instead of making min(n,k) recursive calls, we're making three! Can you use this information to craft a faster solution? You'll have to handle the cases where k > n and k ≤ n differently. Use an example like the one above to figure it out.
My solution is in solution.cpp. The runtime was faster than 12.18% of C++ submissions and the memory usage was less than 21.15% of C++ submissions, which isn't exactly stomping the field, but who cares -- it's fast enough to be accepted!