Leetcode 629.K个逆序对数组

K个逆序对数组

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

逆序对的定义如下:对于数组的第i个和第 j个元素,如果满i < j且 a[i] > a[j],则其为一个逆序对;否则不是。

由于答案可能很大,只需要返回 答案 mod 109 + 7 的值。

示例 1:

输入: n = 3, k = 0

输出: 1

解释:

只有数组 [1,2,3] 包含了从1到3的整数并且正好拥有 0 个逆序对。

示例 2:

输入: n = 3, k = 1

输出: 2

解释:

数组 [1,3,2] 和 [2,1,3] 都有 1 个逆序对。

说明:

  1. n 的范围是 [1, 1000] 并且 k 的范围是 [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]

那么我们就可以写出代码如下了:

 1 class Solution {
 2     public int kInversePairs(int n, int k) {
 3         int M=1000000007;
 4         int[][] dp=new int[n+1][k+1];
 5         dp[0][0]=1;
 6         for(int i=0;i<=n;i++){
 7             for(int j=0;j<i;++j){
 8                 for(int m=0;m<=k;m++){
 9                     if(m-j>=0&&m-j<=k){
10                         dp[i][m]=(dp[i][m]+dp[i-1][m-j])%M;
11                     }
12                 }
13             }
14         }
15         return dp[n][k];
16     }
17 }

我们可以对上面的解法进行时间上的优化,还是来看我们的递推公式:

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的话,就要减去最后一项,这种递推式算起来更高效,减少了一个循环,参见代码如下:

 1 class Solution {
 2     public int kInversePairs(int n, int k) {
 3         int mo=1000000007;
 4         int[][] f=new int[1002][1002];
 5         f[1][0]=1;
 6         for (int i=2;i<=n;i++) {
 7             f[i][0]=1;
 8             for (int j=1;j<=k;j++) {
 9                 f[i][j]=(f[i][j-1]+f[i-1][j])%mo;
10                 if (j>=i) f[i][j]=(f[i][j]-f[i-1][j-i]+mo)%mo;
11             }
12         }
13         return f[n][k];
14     }
15 }

原文地址:https://www.cnblogs.com/kexinxin/p/10381439.html

时间: 2024-08-29 07:00:16

Leetcode 629.K个逆序对数组的相关文章

XJTUOJ wmq的队伍(树状数组求 K 元逆序对)

题目链接:http://oj.xjtuacm.com/problem/14/[分析]二元的逆序对应该都会求,可以用树状数组.这个题要求K元,我们可以看成二元的.我们先从后往前求二元逆序对数, 然后对于每一个数就可以求出在这个数后面的比他小的数的数量.然后我们再加一元时,当前扫到a[i],那么在树状数组中,对于那些比他大的数的 逆序对数+=上一元a[i]的逆序对数. #include <bits/stdc++.h> #define met(a,b) memset(a,b,sizeof a) #d

剑指offer-数组中的逆序对-数组-python

题目描述 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数P.并将P对1000000007取模的结果输出. 即输出P%1000000007 输入描述: 题目保证输入的数组中没有的相同的数字 数据范围: 对于%50的数据,size<=10^4 对于%75的数据,size<=10^5 对于%100的数据,size<=2*10^5 class Solution: def InversePairs(self, data):

(LeetCode)Rotate Array --- 逆置数组

Rotate an array of n elements to the right by k steps. For example, with n = 7 and k = 3, the array [1,2,3,4,5,6,7] is rotated to [5,6,7,1,2,3,4]. Note: Try to come up as many solutions as you can, there are at least 3 different ways to solve this pr

POJ 2299 Ultra-QuickSort(归并排序&#183;逆序对)

题意  给你一个数组求其中逆序对(i<j&&a[i]>a[j])的个数 我们来看一个归并排序的过程: 给定的数组为[2, 4, 5, 3, 1],二分后的数组分别为[2, 4, 5], [1, 3],假设我们已经完成了子过程,现在进行到该数组的"并"操作: a: [2, 4, 5] b: [1, 3] result:[1] 选取b数组的1 a: [2, 4, 5] b: [3] result:[1, 2] 选取a数组的2 a: [4, 5] b: [3] r

HDU 4911 http://acm.hdu.edu.cn/showproblem.php?pid=4911(线段树求逆序对)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4911 解题报告: 给出一个长度为n的序列,然后给出一个k,要你求最多做k次相邻的数字交换后,逆序数最少是多少? 因为每次相邻的交换操作最多只能减少一个逆序对,所以最多可以减少k个逆序对,所以我们只要求出原来的序列有多少个逆序对然后减去k再跟0取较大的就可以了. 因为数据范围是10的五次方,所以暴力求肯定会TLE,所以要用n*logn算法求逆序对,n*logn算法有几种可以求逆序对的: 线段树,树状数

诸城模拟赛 dvd的逆序对

[题目描述] dvd是一个爱序列的孩子. 他对序列的热爱以至于他每天都在和序列度过 但是有一个问题他却一直没能解决 给你n,k求1~n有多少排列有恰好k个逆序对 [输入格式] 一行两个整数n,k [输出格式] 输出一个整数,表示答案对1000000007取模后的结果 [样例输入] 4 1 [样例输出] 3 [样例解释] 1 2 4 3 1 3 2 4 2 1 3 4 [数据规模及约定] 对于10%的数据  n<=10 对于30%的数据  k<=50 对于100%的数据 1<=n,k<

剑指Offer 面试题36:数组中的逆序对及其变形(Leetcode 315. Count of Smaller Numbers After Self)题解

剑指Offer 面试题36:数组中的逆序对 题目:在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数. 例如, 在数组{7,5,6,4}中,一共存在5个逆序对,分别是(7,6),(7,5),(7,4),(6,4)和(5,4),输出5. 提交网址: http://www.nowcoder.com/practice/96bd6684e04a44eb80e6a68efc0ec6c5?tpId=13&tqId=11188 或 htt

数组中的逆序对

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数P.并将P对1000000007取模的结果输出. 即输出P%1000000007 利用归并排序的思想: 归并排序的改进,把数据分成前后两个数组(递归分到每个数组仅有一个数据项),合并数组,合并时,出现前面的数组值array[i]大于后面数组值array[j]时:则前面数组array[i]~array[mid]都是大于array[j]的,count += mid+1 - i.

树状数组求逆序对:POJ 2299、3067

前几天开始看树状数组了,然后开始找题来刷. 首先是 POJ 2299 Ultra-QuickSort: http://poj.org/problem?id=2299 这题是指给你一个无序序列,只能交换相邻的两数使它有序,要你求出交换的次数.实质上就是求逆序对,网上有很多人说它的原理是冒泡排序,可以用归并排序来求出,但我一时间想不出它是如何和归并排序搭上边的(当初排序没学好啊~),只好用刚学过的树状数组来解决了.在POJ 1990中学到了如何在实际中应用上树状数组,没错,就是用个特殊的数组来记录即