算法设计与分析第十二周——动态规划之 K Inverse Pairs Array

本文探讨了LeetCode上的629.KInversePairsArray问题,通过动态规划方法求解由1至n的序列中拥有k个逆序对的序列数量。文章详细解析了状态转移方程,并给出了C++实现代码。

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

算法设计与分析第十二周——动态规划之 K Inverse Pairs Array

      这周的题目:629. K Inverse Pairs Array


题目详情

      题目要求为,给定两个正整数 n 和 k,求由整数 1、2、3、4、...、n 组成的有k个逆序对的不同顺序的序列(序列的原数个数为n,且两两间相异)的个数,其中逆序对的定义为:如果对于一个序列的下标 i 和 j,当 i > j 时,有s[ i ] < s[ j ],则称这个序列有逆序对。

样例分析:

输入输出分析
n = 3, k = 0
1当且仅当序列为{1,2,3}时,存在0个逆序对。比如序列为{3,2,1}时,有逆序对{s[0], s[1]}、{s[0], s[2]}、{s[1], s[2]} 3个。
n = 3, k = 1
2当序列为{1,3,2}或{2,1,3}时,序列有1个逆序对。

题目分析与算法设计

      我们回归到动态规划的本质上,第一,使用动态数组表示状态,第二,如何归纳总结出状态转移方程。定义数组 dp[ i ][ j ] 为由整数1、2、3、...、i 的中存在 j 个逆序对的数量,那么 dp[ n ][ k ] 即为所求。

使用归纳法:

  1. dp[ n ][ k ]已知。
  2. 则对于 dp[ n + 1][ k ],其表示为在由整数 1、2、3、4、...、n+1 组成的有k个逆序对的不同顺序的序列,此时,假设当前 n = 4,对于 n + 1 的情况,我们只需在4个数之间插入一个数即可,即有以下序列:xxxx5、xxx5x、xx5xx、x5xxx、5xxxx。这里的xxxx表示1到4的任意排列,那么第一种情况xxxx5不会增加任何新的逆序对,因为xxxx中没有比5大的数字,而 xxx5x会新增加1个逆序对,xx5xx,x5xxx,5xxxx分别会增加2,3,4个逆序对。那么xxxx5就相当于 dp[ 4 ][ k ],一般的表示即 dp[ n ][ k ],那么依次往后类推,就是 dp[n][k-1], dp[n][k-2]...dp[n][k-n]。
  3. 则 dp[ n + 1 ][ k ] = dp[ n ][ k ] + dp[ n ][ k - 1] + dp[ n ][ k - 2] +...+dp[ n ][ k - n ];
  4. 把 n + 1 代换成 n ,得 dp[ n ][ k ] = dp[ n - 1 ][ k ] + dp[ n - 1 ][ k - 1] + dp[ n - 1 ][ k - 2] +...+dp[ n - 1 ][ k - n  + 1 ]
  5. 更一般的,dp[ i ][ j ] = dp[ i - 1 ][ j ] + dp[ i - 1 ][ j - 1] + dp[ i - 1 ][ j - 2] +...+ dp[ i - 1 ][ j - i  + 1 ] ;(1)

     又由上述递推,有 dp[ i ][ j +1] = dp[ i - 1 ][ j  + 1] + dp[ i - 1 ][ j  + 1 - 1] + dp[ i - 1  ][ j + 1 - 2] +...+dp[ i ][ j + 1 - i  + 1 ];(2),式子(2) - (1) =  dp[ i ][ j +1] - dp[ i ][ j ] = dp[ i - 1 ][ j  + 1] - dp[ i - 1 ][ j - i  + 1 ];化简得到 dp[ i ][ j +1] = dp[ i ][ j ] + dp[ i - 1 ][ j + 1 ] - dp[ i - 1 ][ j - n + 1 ];使用 j 替换 j + 1,得到:dp[ i ][ j ] = dp[ i ][ j - 1 ] + dp[ i - 1 ][ j ] - dp[ i - 1 ][ j - i ];

对于上式:dp[ i ][ j ] = dp[ i ][ j - 1 ] + dp[ i - 1 ][ j ] - dp[ i - 1 ][ j - i ],当且仅当 j >= i 时,dp[ i - 1 ][ j - i ] 才有意义。所以,最终的方程为:

  • dp[ i ][ j ]  = dp[ i ][ j - 1 ] + dp[ i - 1 ][ j ] - dp[ i - 1 ][ j - i ],(i <= j);
  • dp[ i ][ j ]  = dp[ i ][ j - 1 ] + dp[ i - 1 ][ j ] ,(i > j)

代码详情

int kInversePairs(int n, int k) {
  
      	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][j-1] + dp[i-1][j]) % mod;
        		if (j >= i) {
        			dp[i][j] = (dp[i][j] - dp[i-1][j-i] + mod) % mod;
        		}
        	}
        }
        return dp[n][k];
    }

谢谢阅读。

参考资料:

  1. https://leetcode.com/problems/k-inverse-pairs-array/discuss/104825/Shared-my-C++-O(n-*-k)-solution-with-explanation
  2. http://www.cnblogs.com/grandyang/p/7111385.html
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值