1. 程式人生 > 其它 >動態規劃:LeetCode-H0629-K個逆序對陣列

動態規劃:LeetCode-H0629-K個逆序對陣列

題目描述

給出兩個整數 n 和 k,找出所有包含從 1 到 n 的數字,且恰好擁有 k 個逆序對的不同的陣列的個數。

逆序對的定義如下:對於陣列的第i個和第 j個元素,如果滿i < j且 a[i] > a[j],則其為一個逆序對;否則不是。

由於答案可能很大,只需要返回 答案 mod 109 + 7 的值。

示例 1:

輸入: n = 3, k = 0
輸出: 1
解釋: 
只有陣列 [1,2,3] 包含了從1到3的整數並且正好擁有 0 個逆序對。

示例 2:

輸入: n = 3, k = 1
輸出: 2
解釋: 
陣列 [1,3,2] 和 [2,1,3] 都有 1 個逆序對。

說明:

n 的範圍是 [1, 1000] 並且 k 的範圍是 [0, 1000]。

來源:力扣(LeetCode)
連結:https://leetcode-cn.com/problems/k-inverse-pairs-array
著作權歸領釦網路所有。商業轉載請聯絡官方授權,非商業轉載請註明出處。

解題思路

在思考中發現一個規律,
假如現在要求 ni個數的 ki 個逆序對 (假設此時ki < ni),那麼就是

  1. ni-1個數的ki個逆序對插入 ni 這個數字,不需要新增逆序對,將其新增到最後一位即可。即dp[ni-1][ki] += dp[ni-1][ki]

  2. ni-1個數的ki-1個逆序對插入 ni 這個數字,使得新增一個逆序對,只需要把 ni

    插入到最後一個和倒數第二個數的中間就好了,這樣ni只會和之前的最後一個數字形成逆序對。即dp[ni][ki] += dp[ni-1][ki-1]

  3. ni-1個數的ki-2個逆序對中,將ni插入到倒數第二個數和倒數第三個數的中間。ni和最後兩個數字形成逆序對。即dp[ni][ki] += dp[ni-1][ki-2]

.....
以此類推,可以得到

ki < ni 時,
f(ni)(ki) = f(ni-1)(0) + f(ni-1)(1) + f(ni-1)(2) + ... + f(ni-1)(ki-1) + f(ni-1)(ki)

同理,由於 f(ni)(ki-1) = f(ni-1)(0) + ... + f(ni-1)(ki-1)
得:f(ni)(ki) = f(ni)(ki-1) + f(ni-1)(ki)

但是還有特殊情況,ki 大於 ni 時,將ni插入f(ni-1)(0)、...、f(ni-1)(ki-ni)的第一位都不夠滿足 ki 的對數要求,那麼需要將上述公式減去不能滿足的個數
也就是f(ni)(ki) 應該從 f(ni-1)(ki-ni+1) 開始算起

即: f(ni)(ki) = f(ni-1)(ki-ni+1) + ... + f(ni-1)(ki-1) + f(ni-1)(ki)
同理,f(ni)(ki-1) = f(ni-1)(ki-ni) + ... + f(ni-1)(ki-1)
得:f(ni)(ki)= f(ni)(ki-1) - f(ni-1)(ki-ni) + f(ni-1)(ki)

綜合兩種情況,得到狀態轉移方程為:f(ni)(ki)= f(ni)(ki-1) - (ki>=ni?f(ni-1)(ki-ni):0) + f(ni-1)(ki)

反思錯誤

1、一開始沒有考慮到 ki 大於 ni 的情況,導致數量過多
2、一開始使用 mod 求餘,但是有出現數值為負數的情況,由於減的數量沒有到達求餘等級,但是加的數量求過餘了,這樣就變成負數了。後來使用加減來求餘

Java程式碼

class Solution {
    private static final int MOD = (int)1e9 + 7;
    public int kInversePairs(int n, int k) {
        int[][] cnt = new int[2][k+1];
        cnt[0][0] = 1;// >0數字 0逆序對 -》 1組
        cnt[1][0] = 1;// 1數字 0逆序對 -》 1組
        // ni 幾個數字,,ki 幾組逆序對
        for(int ni=2;ni<=n;++ni) { // 從兩個數字開始
            int cur = ni&1, pre = cur^1;
            for(int ki=1;ki<=k;++ki) { // 從有一個逆序對開始
                cnt[cur][ki] = cnt[cur][ki-1] + cnt[pre][ki] - (ki>=ni?cnt[pre][ki-ni]:0);
                if(cnt[cur][ki] >= MOD) {
                    cnt[cur][ki] -= MOD;
                } else if (cnt[cur][ki]<0) {
                    cnt[cur][ki] += MOD;
                }
            }
        }
        return cnt[n&1][k];
    }
}

執行結果

耗時:14ms,記憶體:35.3MB