629. K Inverse Pairs Array 自制答案

给定两个整数n和k,找出从1到n的所有数组中恰好有k个逆序对的数量。逆序对定义为数组中i < j且a[i] > a[j]的元素对。由于答案可能很大,需要对10^9 + 7取模。例如,n=3, k=0时,只有[1,2,3]满足条件。" 50354497,4108965,TCP Socket通信:服务器与安卓客户端的交互实践,"['网络开发', 'TCP', 'socket编程', '安卓应用开发', '服务器开发']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Question

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 ij 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.动态规划

首先,Inverse Pairs Array这个单词翻译到中文大概是“逆对数组”,虽然听起来有点别扭但我们就按照这个翻译好了。

在定义中,数组a中存在第i个和第j个元素,且i<j但a[i]>a[j],则是一个逆序对。而K Inverse Pairs Array 则是存在k个逆序对的数组。

在这道题的计算中,需要计算前n个数字组成存在k个逆序对的数组的不同排列数量,而这与动态规划息息相关。

当我们添加第n个数字的时候,其目的是为了满足k个逆序对,那么就将有如下几种可能性:

当n处于最后一位,即本身的位置时,没有增加新的逆序对,那么就应该找到前(n-1)个数字时,出现k个逆序对的情况。
当n处于倒数第二位时,增加了一个逆序对,那么就应该找到前(n-1)个数字时,出现(k-1)个逆序对的情况。
…………………………
同理,当n处于第一位的时候,增加了(n-1)个逆序对,那么就应该找到前(n-1)个数字时,出现(k-(n-1))个逆序对的情况。

在这里我们通常使用一个二维数组dp[n][k]来表示具体情况。表示前n个数字组成存在k个逆序对数组的排列数量,有点像最开始学习背包问题的表达。

那么我们将得到如下公式:
 dp[n][k] = dp[n-1][k]+dp[n-1][k-1]+dp[n-1][k-2]+...+dp[n-1][k+1-n+1]+dp[n-1][k-n+1]
 dp[n][k+1] = dp[n-1][k+1]+dp[n-1][k]+dp[n-1][k-1]+dp[n-1][k-2]+...+dp[n-1][k+1-n+1]

上下两个公式同时相减,可以得到下式:

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

上面的公式,基本上就是程序中编写的主要部分,但一定要注意的是,k-(n-1)可能会小于0,需要进行处理,当小于0时,dp[n-1][k+1-n]赋值为0即可。

 public int kInversePairs(int n, int k) {
        long[][] dp = new long[n + 1][k + 1];
        int mod = 1000000007;
        for (int i = 1; i <= n; i++) {
            if(i==1){
                dp[i][0]=1;
                continue;
            }else{
                dp[i][0]=1;
            }
            for (int j = 1; j <= k; j++) {

                dp[i][j] = dp[i][j-1] + dp[i-1][j];
                dp[i][j] =(j>=i?(dp[i][j] - dp[i-1][j-i]):dp[i][j]);
                dp[i][j] = (dp[i][j]+mod) % mod;
            }
        }
        return (int)dp[n][k];
    }

2. 简单优化

对于整数n来说,它能拥有的最多逆序对是有限的,这种极端情况会出现在所有的元素都变动最大的情况下,即降序排列的数组。在这种情况下,逆序对拥有n-1+n-2……1
根据等差数列求和公式,则拥有n*(n-1)/2种情况。

在程序中,如果循环中的j值大于了n*(n-1)/2,那么这种情况是不可能出现的。所以,我们可以用j<=i*(i-1)/2来约束循环,减少无用的计算。

与此同时,题目要求要对10的九次幂+7取余,在我的操作中实际上是用了long型数组取巧了,根据正规要求,应该对每一步要求都取余。
所以最后的代码应该如下:
    public int kInversePairs(int n, int k) {
        if (k > n*(n-1)/2 || k < 0) return 0;
        if (k == 0 || k == n*(n-1)/2) return 1;
        int[][] dp = new int[n + 1][k + 1];
        int mod = 1000000007;
        for (int i = 1; i <= n; i++) {
            if(i==1){
                dp[i][0]=1;
                continue;
            }else{
                dp[i][0]=1;
            }
            for (int j = 1; j <= k && j <= i * (i - 1) / 2; j++) {

                dp[i][j] = (dp[i][j-1] + dp[i-1][j])%mod;
                dp[i][j] =(j>=i?(dp[i][j] +mod - dp[i-1][j-i]):dp[i][j])%mod;
                dp[i][j] = (dp[i][j]+mod) % mod;
            }
        }
        return (int)dp[n][k];
    }





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值