算法设计与分析第十二周——动态规划之 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 ],则称这个序列有逆序对。
样例分析:
输入 | 输出 | 分析 |
| 1 | 当且仅当序列为{1,2,3}时,存在0个逆序对。比如序列为{3,2,1}时,有逆序对{s[0], s[1]}、{s[0], s[2]}、{s[1], s[2]} 3个。 |
| 2 | 当序列为{1,3,2}或{2,1,3}时,序列有1个逆序对。 |
题目分析与算法设计
我们回归到动态规划的本质上,第一,使用动态数组表示状态,第二,如何归纳总结出状态转移方程。定义数组 dp[ i ][ j ] 为由整数1、2、3、...、i 的中存在 j 个逆序对的数量,那么 dp[ n ][ k ] 即为所求。
使用归纳法:
- dp[ n ][ k ]已知。
- 则对于 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]。
- 则 dp[ n + 1 ][ k ] = dp[ n ][ k ] + dp[ n ][ k - 1] + dp[ n ][ k - 2] +...+dp[ n ][ k - n ];
- 把 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 ]
- 更一般的,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];
}
谢谢阅读。
参考资料: