LeetCode 629. K Inverse Pairs Array
阿新 • • 發佈:2021-02-19
# LeetCode 629. K Inverse Pairs Array
## 題目描述
[題目連結](https://leetcode.com/problems/k-inverse-pairs-array/)
### 暴力解
定義dp[i][j]表示:1.....i範圍內,形成j個逆序對有多少種方式,那麼i和j的範圍分別是:
i: [1...n]
j: [0...k]
其中我們把dp[0][...]位置棄而不用,因為沒有意義,我們需要填好dp這個二維陣列,並且返回dp[n][k]的值
dp[n][k]: 1...n範圍內,生成k個逆序對有多少種方式,正好是題意要求。
由此可知,第0列的值是1,因為第0列表示1...i範圍內,形成0個逆序對的陣列有多少,只有一個(就是按順序排列的那個)
```java
int[][] dp = new int[n + 1][k + 1];
for (int i = 1; i <= n; i++) {
// 1....i範圍內,形成0個逆序對的陣列只有一個(按順序排列那個)
dp[i][0] = 1;
}
```
對於普遍位置,按照i依次從i位置放到1位置,一共可以生成多少逆序對來做
比如:
通過7放在哪個位置來確定,此時要分兩種情況
情況1:
dp[7][3]
7放倒數第一,dp[7][3] = dp[6][3] **// 7放倒數第一,所以1..7產生的逆序對和1...6產生的逆序對一樣,以下同理**
7放倒數第二,dp[7][3] = dp[6][2]
7放倒數第三,dp[7][3] = dp[6][1]
7放倒數第四,dp[7][3] = dp[6][0]
情況2:
dp[7][7]
7放倒數第一,dp[7][7] = dp[6][7]
7放倒數第二,dp[7][7] = dp[6][6]
7放倒數第三,dp[7][7] = dp[6][5]
7放倒數第四,dp[7][7] = dp[6][4]
7放倒數第五,dp[7][7] = dp[6][3]
7放倒數第六,dp[7][7] = dp[6][2]
7放倒數第七,dp[7][7] = dp[6][1]
所以:
```java
// dp[i][j] 普遍位置
// 按照i依次從i位置放到1位置,一共可以生成多少逆序對來做
for (int i = 2; i <= n; i++) {
for (int j = 1; j <= k; j++) {
// 通過7放在哪個位置來確定
// 情況1:dp[7][3]
// 7放倒數第一,dp[7][3] = dp[6][3]
// 7放倒數第二,dp[7][3] = dp[6][2]
// 7放倒數第三,dp[7][3] = dp[6][1]
// 7放倒數第四,dp[7][3] = dp[6][0]
// 情況2:dp[7][7]
// 7放倒數第一,dp[7][7] = dp[6][7]
// 7放倒數第二,dp[7][7] = dp[6][6]
// 7放倒數第三,dp[7][7] = dp[6][5]
// 7放倒數第四,dp[7][7] = dp[6][4]
// 7放倒數第五,dp[7][7] = dp[6][3]
// 7放倒數第六,dp[7][7] = dp[6][2]
// 7放倒數第七,dp[7][7] = dp[6][1]
for (int l = j; l >= Math.max(0, j - i + 1); l--) {
dp[i][j] += dp[i - 1][l];
dp[i][j] %= MOD;
}
}
}
```
完整程式碼如下,但是這個方法會超時,
```java
public static int kInversePairs(int n, int k) {
// dp[i][j] : 1 ...i 範圍內,形成j個逆序對有多少種方式
// dp[0][...] 棄而不用,因為沒有意義
int[][] dp = new int[n + 1][k + 1];
for (int i = 1; i <= n; i++) {
// 1....i範圍內,形成0個逆序對的陣列只有一個(按順序排列那個)
dp[i][0] = 1;
}
// dp[i][j] 普遍位置
// 按照i依次從i位置放到1位置,一共可以生成多少逆序對來做
for (int i = 2; i <= n; i++) {
for (int j = 1; j <= k; j++) {
// 通過7放在哪個位置來確定
// 情況1:dp[7][3]
// 7放倒數第一,dp[7][3] = dp[6][3]
// 7放倒數第二,dp[7][3] = dp[6][2]
// 7放倒數第三,dp[7][3] = dp[6][1]
// 7放倒數第四,dp[7][3] = dp[6][0]
// 情況2:dp[7][7]
// 7放倒數第一,dp[7][7] = dp[6][7]
// 7放倒數第二,dp[7][7] = dp[6][6]
// 7放倒數第三,dp[7][7] = dp[6][5]
// 7放倒數第四,dp[7][7] = dp[6][4]
// 7放倒數第五,dp[7][7] = dp[6][3]
// 7放倒數第六,dp[7][7] = dp[6][2]
// 7放倒數第七,dp[7][7] = dp[6][1]
for (int l = j; l > = Math.max(0, j - i + 1); l--) {
dp[i][j] += dp[i - 1][l];
dp[i][j] %= MOD;
}
}
}
return dp[n][k];
}
```
### 優化
優化部分在於如下這個迴圈
```java
for (int l = j; l >= Math.max(0, j - i + 1); l--) {
dp[i][j] += dp[i - 1][l];
dp[i][j] %= MOD;
}
```
我們還是以例子說明:
情況1:
dp[7][3]
7放倒數第一,dp[7][3] = dp[6][3]
7放倒數第二,dp[7][3] = dp[6][2]
7放倒數第三,dp[7][3] = dp[6][1]
7放倒數第四,dp[7][3] = dp[6][0]
dp[7][4]
7放倒數第一,dp[7][4] = dp[6][4]
7放倒數第二,dp[7][4] = dp[6][3]
7放倒數第三,dp[7][4] = dp[6][2]
7放倒數第四,dp[7][4] = dp[6][1]
7放倒數第五,dp[7][4] = dp[6][0]
所以 情況1:
**dp[i][j] = dp[i][j-1] + dp[i-1][j]**
對於情況2(j> =i 下發生):
dp[7][9]
7放倒數第一,dp[7][9] = dp[6][9]
7放倒數第二,dp[7][9] = dp[6][8]
7放倒數第三,dp[7][9] = dp[6][7]
7放倒數第四,dp[7][9] = dp[6][6]
7放倒數第五,dp[7][9] = dp[6][5]
7放倒數第六,dp[7][9] = dp[6][4]
7放倒數第七,dp[7][9] = dp[6][3]
dp[7][8]
7放倒數第一,dp[7][8] = dp[6][8]
7放倒數第二,dp[7][8] = dp[6][7]
7放倒數第三,dp[7][8] = dp[6][6]
7放倒數第四,dp[7][8] = dp[6][5]
7放倒數第五,dp[7][8] = dp[6][4]
7放倒數第六,dp[7][8] = dp[6][3]
7放倒數第七,dp[7][8] = dp[6][2]
**dp[i][j] =dp[i][j] - dp[i - 1][j - i] **
**
優化版本的程式碼如下:
```java
// 優化版本
public static int kInversePairs(int n, int k) {
// dp[i][j] : 1 ...i 範圍內,形成j個逆序對有多少種方式
// dp[0][...] 棄而不用,因為沒有意義
int[][] dp = new int[n + 1][k + 1];
for (int i = 1; i <= n; i++) {
// 1....i範圍內,形成0個逆序對的陣列只有一個(按順序排列那個)
dp[i][0] = 1;
}
// dp[i][j] 普遍位置
// 按照i依次從i位置放到1位置,一共可以生成多少逆序對來做
for (int i = 2; i <= n; i++) {
for (int j = 1; j <= k; j++) {
// 優化
// 情況1:
// dp[7][3]
// 7放倒數第一,dp[7][3] = dp[6][3]
// 7放倒數第二,dp[7][3] = dp[6][2]
// 7放倒數第三,dp[7][3] = dp[6][1]
// 7放倒數第四,dp[7][3] = dp[6][0]
// dp[7][4]
// 7放倒數第一,dp[7][4] = dp[6][4]
// 7放倒數第二,dp[7][4] = dp[6][3]
// 7放倒數第三,dp[7][4] = dp[6][2]
// 7放倒數第四,dp[7][4] = dp[6][1]
// 7放倒數第五,dp[7][4] = dp[6][0]
// 所以 情況1: dp[i][j] = dp[i][j-1] + dp[i-1][j]
// 情況2:dp[7][9]
// 7放倒數第一,dp[7][9] = dp[6][9]
// 7放倒數第二,dp[7][9] = dp[6][8]
// 7放倒數第三,dp[7][9] = dp[6][7]
// 7放倒數第四,dp[7][9] = dp[6][6]
// 7放倒數第五,dp[7][9] = dp[6][5]
// 7放倒數第六,dp[7][9] = dp[6][4]
// 7放倒數第七,dp[7][9] = dp[6][3]
// dp[7][8]
// 7放倒數第一,dp[7][8] = dp[6][8]
// 7放倒數第二,dp[7][8] = dp[6][7]
// 7放倒數第三,dp[7][8] = dp[6][6]
// 7放倒數第四,dp[7][8] = dp[6][5]
// 7放倒數第五,dp[7][8] = dp[6][4]
// 7放倒數第六,dp[7][8] = dp[6][3]
// 7放倒數第七,dp[7][8] = dp[6][2]
// 情況2 : j> =i 下發生
dp[i][j] = (dp[i - 1][j] + dp[i][j - 1]) % MOD;
if (j >= i) {
dp[i][j] = (dp[i][j] - dp[i - 1][j - i] + MOD) % MOD;
}
}
}
return dp[n][k];
}
```
## 更多
[演算法和資料結構筆記](https://github.com/GreyZeng/algorithm)
## 參考資料
- [程式設計師程式碼面試指南(第2版)](https://book.douban.com/subject/30422021/)
- [演算法和資料結構基礎班-左程雲](https://ke.qq.com/course/2145184)
- [極客時間-資料結構與演算法之美-王爭](https://time.geekbang.org/column/intro/126)
- [演算法(第四版)](https://book.douban.com/subject/199