Skip to content

【动规】逆序对为k的排列个数

k inverse pairs array

给出两个整数 nk,找出所有包含从 1n 的数字,且恰好拥有 k 个逆序对的不同的数组的个数。

f(i,j)代表前i个数组成的序列中含有j个逆序对的个数。

假设我们知道f(i1,), 考虑将i插入到k=0i1的位置处得到的逆序对数量为i1k,从而:

f(i,j)=k=0i1f(i1,j(i1k))=k=0i1f(i1,jk)

展开可发现:

f(i,j)=f(i1,j)+f(i1,j1)++f(i1,ji+1)f(i,j1)=f(i1,j1)++f(i1,ji+1)+f(i1,ji)

从而:

f(i,j)=f(i1,j)+f(i,j1)f(i1,ji)

边界条件:

f(,0)=1specifically, f(1,0)=1f(1,j)=0if j>0f(,j)=0if j<0

动态规划:

class Solution {
public:
    const static int M = 1e9 + 7;
    int kInversePairs(int n, int k) {
        vector<vector<long long>> dp(n+1, vector<long long>(k+1, 0));
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j <= k; j++) {
                if (j == 0) dp[i][j] = 1;
                else if (i == 1) dp[i][j] = 0;
                else {
                    dp[i][j] = (dp[i-1][j] + dp[i][j-1]) % M;
                    if (j - i >= 0) dp[i][j] = (dp[i][j] - dp[i-1][j-i] + M) % M;
                }

            }
        }
        return dp[n][k];
    }
};

空间优化:

class Solution {
public:
    static const int mod = 1000000007;
    int kInversePairs(int n, int k) {
        int f[2][k + 1];
        memset(f, 0, sizeof(f));

        f[1][0] = 1;
        for (int i = 2; i <= n; ++i) {
            int flip = i & 1;
            int sum = 0;
            for (int j = 0; j <= k; ++j) {
                sum += f[1 - flip][j];
                if (j >= i) sum -= f[1 - flip][j - i];
                if (sum < 0) sum += mod;
                if (sum >= mod) sum -= mod;
                f[flip][j] = sum;
            }
        }
        return f[n & 1][k];
    }
};