[LeetCode] K Inverse Pairs Array K個翻轉對數組


 

Given two integers n and k, find how many different arrays consist of numbers from 1 to n such that there are exactly k inverse pairs.

We define an inverse pair as following: For ith and jth element in the array, if i < j and a[i] > a[j] then it's an inverse pair; Otherwise, it's not.

Since the answer may very large, the answer should be modulo 109 + 7.

Example 1:

Input: n = 3, k = 0
Output: 1
Explanation: 
Only the array [1,2,3] which consists of numbers from 1 to 3 has exactly 0 inverse pair.

 

Example 2:

Input: n = 3, k = 1
Output: 2
Explanation: 
The array [1,3,2] and [2,1,3] have exactly 1 inverse pair.

 

Note:

  1. The integer n is in the range [1, 1000] and k is in the range [0, 1000].

 

這道題給了我們1到n總共n個數字,讓我們任意排列數組的順序,使其剛好存在k個翻轉對,所謂的翻轉對,就是位置在前面的數字值大,而且題目中表明了結果會很大很大,要我們對一個很大的數字取余。對於這種結果巨大的題目,勸君放棄暴力破解或者是無腦遞歸,想都不用想,那么最先應該考慮的就是DP的解法了。我們需要一個二維的DP數組,其中dp[i][j]表示1到i的數字中有j個翻轉對的排列總數,那么我們要求的就是dp[n][k]了,即1到n的數字中有k個翻轉對的排列總數。現在難點就是要求遞推公式了。我們想如果我們已經知道dp[n][k]了,怎么求dp[n+1][k],先來看dp[n+1][k]的含義,是1到n+1點數字中有k個翻轉對的個數,那么實際上在1到n的數字中的某個位置加上了n+1這個數,為了簡單起見,我們先讓n=4,那么實際上相當於要在某個位置加上5,那么加5的位置就有如下幾種情況:

xxxx5

xxx5x

xx5xx

x5xxx

5xxxx

這里xxxx表示1到4的任意排列,那么第一種情況xxxx5不會增加任何新的翻轉對,因為xxxx中沒有比5大的數字,而 xxx5x會新增加1個翻轉對,xx5xx,x5xxx,5xxxx分別會增加2,3,4個翻轉對。那么xxxx5就相當於dp[n][k],即dp[4][k],那么依次往前類推,就是dp[n][k-1], dp[n][k-2]...dp[n][k-n],這樣我們就可以得出dp[n+1][k]的求法了:

dp[n+1][k] = dp[n][k] + dp[n][k-1] + ... + dp[n][k - n]

那么dp[n][k]的求法也就一目了然了:

dp[n][k] = dp[n - 1][k] + dp[n - 1][k-1] + ... + dp[n - 1][k - n + 1]

那么我們就可以寫出代碼如下了:

 

解法一:

class Solution {
public:
    int kInversePairs(int n, int k) {
        int M = 1000000007;
        vector<vector<int>> dp(n + 1, vector<int>(k + 1, 0));
        dp[0][0] = 1;
        for (int i = 0; i <= n; ++i) {
            for (int j = 0; j < i; ++j) {
                for (int m = 0; m <= k; ++m) {
                    if (m - j >= 0 && m - j <= k) {
                        dp[i][m] = (dp[i][m] + dp[i - 1][m - j]) % M;
                    }
                }
            }
        }
        return dp[n][k];
    }
};

 

我們可以對上面的解法進行時間上的優化,還是來看我們的遞推公式: 

dp[n][k] = dp[n - 1][k] + dp[n - 1][k-1] + ... + dp[n - 1][k - n + 1]

我們可以用k+1代替k,得到:

dp[n][k+1] = dp[n - 1][k+1] + dp[n - 1][k] + ... + dp[n - 1][k + 1 - n + 1]

用第二個等式減去第一個等式可以得到:

dp[n][k+1] = dp[n][k] + dp[n - 1][k+1] - dp[n - 1][k - n + 1]

將k+1換回成k,可以得到:

dp[n][k] = dp[n][k-1] + dp[n - 1][k] - dp[n - 1][k - n]

我們可以發現當k>=n的時候,最后一項的數組坐標才能為非負數,從而最后一項才有值,所以我們再更新的時候只需要判斷一下k和n的關系,如果k>=n的話,就要減去最后一項,這種遞推式算起來更高效,減少了一個循環,參見代碼如下:

 

解法二:

class Solution {
public:
    int kInversePairs(int n, int k) {
        int M = 1000000007;
        vector<vector<int>> dp(n + 1, vector<int>(k + 1, 0));
        dp[0][0] = 1;
        for (int i = 1; i <= n; ++i) {
            dp[i][0] = 1;
            for (int j = 1; j <= k; ++j) {
                dp[i][j] = (dp[i - 1][j] + dp[i][j - 1]) % M;
                if (j >= i) {
                    dp[i][j] = (dp[i][j] - dp[i - 1][j - i] + M) % M;
                }
            }
        }
        return dp[n][k];
    }
};

 

參考資料:

https://discuss.leetcode.com/topic/93815/java-dp-o-nk-solution/2

https://discuss.leetcode.com/topic/93765/shared-my-c-o-n-k-solution-with-explanation

 

LeetCode All in One 題目講解匯總(持續更新中...) 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM