Skip to content

Commit a19fe89

Browse files
committed
LeetCode 629. K Inverse Pairs Array
1 parent d30d434 commit a19fe89

File tree

2 files changed

+122
-0
lines changed

2 files changed

+122
-0
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ Proposed solutions to some LeetCode problems. The first column links to the prob
6868
| [589. N-ary Tree Preorder Traversal][lc589] | Easy | [![python](res/py.png)](leetcode/n-ary-tree-preorder-traversal.py) |
6969
| [596. Classes More Than 5 Students][lc596] | Easy | [![mysql](res/mysql.png)](leetcode/classes_more_than_5_students.sql) |
7070
| [599. Minimum Index Sum of Two Lists][lc599] | Easy | [![python](res/py.png)](leetcode/minimum-index-sum-of-two-lists.py) |
71+
| [629. K Inverse Pairs Array][lc629] | Hard | [![python](res/py.png)][lc629py] |
7172
| [630. Course Schedule III][lc630] | Hard | [![python](res/py.png)](leetcode/course-schedule-iii.py) |
7273
| [665. Non-decreasing Array][lc665] | Medium | [![python](res/py.png)](leetcode/non-decreasing-array.py) |
7374
| [695. Max Area of Island][lc695] | Medium | [![python](res/py.png)][lc695py] |
@@ -172,6 +173,7 @@ First column is the problem difficulty, in descending order, second links to the
172173
[lc589]: https://leetcode.com/problems/n-ary-tree-preorder-traversal/
173174
[lc596]: https://leetcode.com/problems/classes-more-than-5-students/
174175
[lc599]: https://leetcode.com/problems/minimum-index-sum-of-two-lists/
176+
[lc629]: https://leetcode.com/problems/k-inverse-pairs-array/
175177
[lc630]: https://leetcode.com/problems/course-schedule-iii/
176178
[lc665]: https://leetcode.com/problems/non-decreasing-array/
177179
[lc695]: https://leetcode.com/problems/max-area-of-island/
@@ -207,6 +209,7 @@ First column is the problem difficulty, in descending order, second links to the
207209
[lc56py]: leetcode/merge-intervals.py
208210
[lc105py]: leetcode/construct-binary-tree-from-preorder-and-inorder-traversal.py
209211
[lc576py]: leetcode/out-of-boundary-paths.py
212+
[lc629py]: leetcode/k-inverse-pairs-array.py
210213
[lc695py]: leetcode/max-area-of-island.py
211214
[lc1647py]: leetcode/minimum-operations-to-reduce-x-to-zero.py
212215
[lc1658py]: leetcode/minimum-operations-to-reduce-x-to-zero.py

leetcode/k-inverse-pairs-array.py

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# https://leetcode.com/problems/k-inverse-pairs-array/
2+
3+
# Tags: Dynamic Programming
4+
5+
6+
import timeit
7+
8+
9+
# We can see that the number of ways to invert pairs will be equal to the number of ways
10+
# to invert pairs for n-1 and each value of k up to n-1
11+
#
12+
# The obvious solution is bottom-up tabulation, but we can first look at memoization
13+
#
14+
# Time complexity: O(nk min(n,k)) we call kInversePairs for each position, each calls costs min(n,k)
15+
# since already computed values will not be recalculated
16+
# Space complexity: O(nk) for the memo
17+
#
18+
# Time limit exceeded
19+
class Memoization:
20+
def kInversePairs(self, n: int, k: int) -> int:
21+
MOD = 10**9 + 7
22+
23+
# Create the memo if it does not exist
24+
if not hasattr(self, "memo"):
25+
self.memo = [[None for _ in range(k + 1)] for _ in range(n + 1)]
26+
# Base cases
27+
self.memo[0][0] = 0
28+
for j in range(1, n + 1):
29+
self.memo[j][0] = 1
30+
31+
# If the value is found, do not recalculate
32+
if self.memo[n][k] is not None:
33+
return self.memo[n][k]
34+
35+
# Calculate the sum of previous n and k [0..n-1]
36+
result = 0
37+
i = n - 1
38+
for j in range(min(k, i) + 1):
39+
result += self.kInversePairs(i, k - j)
40+
41+
self.memo[n][k] = result
42+
return result % MOD
43+
44+
45+
# We can calculate the results bottom-up
46+
#
47+
# Time complexity: O(n*k*min(n-1, k))
48+
# Space complexity: O(n*k)
49+
#
50+
# Time limit exceeded
51+
class Tabulation:
52+
def kInversePairs(self, n: int, k: int) -> int:
53+
MOD = 10**9 + 7
54+
55+
# Create a matrix to store the results
56+
dp = [[0 for _ in range(k + 1)] for _ in range(n + 1)]
57+
for i in range(n + 1):
58+
for j in range(k + 1):
59+
if j == 0:
60+
dp[i][j] = 1
61+
else:
62+
for p in range(min(j, i - 1) + 1):
63+
# Sum the previous results for n-1 and p in 0 min(j, i-1)
64+
dp[i][j] = dp[i][j] + dp[i - 1][j - p]
65+
66+
# We could save each result with % MOD but it is quicker to do it once and python will not overflow
67+
return dp[n][k] % MOD
68+
69+
70+
# Optimize the previous solution using only one array to store results for the last value of n
71+
#
72+
# Time complexity: O(n*k) for each n iterates twice over k
73+
# Space complexity: O(1) it only stores one array of size k+1
74+
#
75+
# Runtime: 551 ms, faster than 70.00% of Python3 online submissions for K Inverse Pairs Array.
76+
# Memory Usage: 14.2 MB, less than 72.50% of Python3 online submissions for K Inverse Pairs Array.
77+
class OptTabulation:
78+
def kInversePairs(self, n: int, k: int) -> int:
79+
dp = [1] + [0] * k
80+
for i in range(2, n + 1):
81+
for j in range(1, k + 1):
82+
dp[j] += dp[j - 1]
83+
for j in range(k, 0, -1):
84+
dp[j] -= j - i >= 0 and dp[j - i]
85+
return dp[k] % (10**9 + 7)
86+
87+
88+
def test():
89+
executors = [
90+
Memoization,
91+
Tabulation,
92+
OptTabulation,
93+
]
94+
tests = [
95+
[5, 4, 20],
96+
[3, 0, 1],
97+
[3, 1, 2],
98+
[1, 0, 1],
99+
[1, 1, 0],
100+
[90, 90, 547544970],
101+
# [1000, 1000, 663677020],
102+
]
103+
for executor in executors:
104+
start = timeit.default_timer()
105+
for _ in range(int(float("1"))):
106+
for i, t in enumerate(tests):
107+
sol = executor()
108+
result = sol.kInversePairs(t[0], t[1])
109+
exp = t[2]
110+
assert (
111+
result == exp
112+
), f"\033[93m» {result} <> {exp}\033[91m for test {i} using \033[1m{executor.__name__}"
113+
stop = timeit.default_timer()
114+
used = str(round(stop - start, 5))
115+
res = "{0:20}{1:10}{2:10}".format(executor.__name__, used, "seconds")
116+
print(f"\033[92m» {res}\033[0m")
117+
118+
119+
test()

0 commit comments

Comments
 (0)