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 个逆序对。
说明:
- 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]
那么我们就可以写出代码如下了:
class Solution {
public int kInversePairs(int n, int k) {
int M=1000000007;
int[][] dp=new int[n+1][k+1];
dp[0][0]=1;
for(int i=0;i<=n;i++){
for(int j=0;j<i;++j){
for(int m=0;m<=k;m++){
if(m-j>=0&&m-j<=k){
dp[i][m]=(dp[i][m]+dp[i-1][m-j])%M;
}
}
}
}
return dp[n][k];
}
}
我们可以对上面的解法进行时间上的优化,还是来看我们的递推公式:
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的话,就要减去最后一项,这种递推式算起来更高效,减少了一个循环,参见代码如下:
class Solution {
public int kInversePairs(int n, int k) {
int mo=1000000007;
int[][] f=new int[1002][1002];
f[1][0]=1;
for (int i=2;i<=n;i++) {
f[i][0]=1;
for (int j=1;j<=k;j++) {
f[i][j]=(f[i][j-1]+f[i-1][j])%mo;
if (j>=i) f[i][j]=(f[i][j]-f[i-1][j-i]+mo)%mo;
}
}
return f[n][k];
}
}
Leetcode 629.K个逆序对数组的更多相关文章
- Java实现 LeetCode 629 K个逆序对数组(动态规划+数学)
629. K个逆序对数组 给出两个整数 n 和 k,找出所有包含从 1 到 n 的数字,且恰好拥有 k 个逆序对的不同的数组的个数. 逆序对的定义如下:对于数组的第i个和第 j个元素,如果满i < ...
- [Swift]LeetCode629. K个逆序对数组 | K Inverse Pairs Array
Given two integers n and k, find how many different arrays consist of numbers from 1 to n such that ...
- XJTUOJ wmq的队伍(树状数组求 K 元逆序对)
题目链接:http://oj.xjtuacm.com/problem/14/[分析]二元的逆序对应该都会求,可以用树状数组.这个题要求K元,我们可以看成二元的.我们先从后往前求二元逆序对数, 然后对于 ...
- 剑指offer-数组中的逆序对-数组-python
题目描述 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数P.并将P对1000000007取模的结果输出. 即输出P%1000 ...
- 【leetcode dp】629. K Inverse Pairs Array
https://leetcode.com/problems/k-inverse-pairs-array/description/ [题意] 给定n和k,求正好有k个逆序对的长度为n的序列有多少个,0& ...
- HDU 4911 http://acm.hdu.edu.cn/showproblem.php?pid=4911(线段树求逆序对)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4911 解题报告: 给出一个长度为n的序列,然后给出一个k,要你求最多做k次相邻的数字交换后,逆序数最少 ...
- luogu 1521-求逆序对
题意: 逆序对指在一个序列中ai>aj && i < j,也就是一前一后两个数,当大的在前面的时候即算一对. 题目求在一个由1-n组成的序列中逆序对为k的序列的个数. 出题 ...
- 诸城模拟赛 dvd的逆序对
[题目描述] dvd是一个爱序列的孩子. 他对序列的热爱以至于他每天都在和序列度过 但是有一个问题他却一直没能解决 给你n,k求1~n有多少排列有恰好k个逆序对 [输入格式] 一行两个整数n,k [输 ...
- 力扣Leetcode 面试题51. 数组中的逆序对 - 归并排序
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数. 示例 1: 输入: [7,5,6,4] 输出: 5 限制: 0 <= ...
随机推荐
- 数据字典的设计--4.DOM对象的ajax应用
需求:点击下拉选项框,选择一个数据类型,在表单中自动显示该类型下所有的数据项的名称,即数据库中同一keyword对应的所有不重复的ddlName. 1.在dictionaryIndex.js ...
- hihoCoder 挑战赛10 #1144 : 01串
思路:这只是逻辑测试题吧,考虑周全就行了.考虑n=m的情况,n>m的情况,m>n的情况. (1)n>m的情况,0比1多几个都是行的,一共有m个“01”,后面补足够多个零即可. (2) ...
- Codeforces 763A. Timofey and a tree
A. Timofey and a tree 题意:给一棵树,要求判断是否存在一个点,删除这个点后,所有连通块内颜色一样.$N,C \le 10^5$ 想法:这个叫换根吧.先求出一个点合法即其儿子的子树 ...
- 使用SAP云平台 + JNDI访问Internet Service
以Internet Service http://maps.googleapis.com/maps/api/distancematrix/xml?origins=Walldorf&destin ...
- JavaScript: apply , call 方法
我在一开始看到javascript的函数apply和call时,非常的模糊,看也看不懂,最近在网上看到一些文章对apply方法和call的一些示例,总算是看的有点眉目了,在这里我做如下笔记,希望和大家 ...
- NYOJ-22-素数求和问题
原题地址 素数求和问题 描述 现在给你N个数(0<N<1000),现在要求你写出一个程序,找出这N个数中的所有素数,并求和. 输入 第一行给出整数M(0<M<10)代表多少组测 ...
- Python风格规范-FYI
Python风格规范 分号 Tip 不要在行尾加分号, 也不要用分号将两条命令放在同一行. 行长度 Tip 每行不超过80个字符 例外: 长的导入模块语句 注释里的URL 不要使用反斜杠连接行. Py ...
- JavaScript操作Array对象常用的方法
转换方法 因为JavaScript内部机制(继承),所有的对象都具有toLocalString() .toString().valueOf()方法,Array也不例外so:var colors = ...
- click
click简介 Click是一个Python包,用于以可组合的方式创建漂亮的命令行界面,只需要很少的代码.这是“命令行界面创建工具包”.它具有高度可配置性,但具有开箱即用的合理默认值. 点击三点: 任 ...
- C#的接口基础教程之六 接口转换
C#中不仅支持.Net 平台,而且支持COM平台.为了支持 COM和.Net,C# 包含一种称为属性的独特语言特性.一个属性实际上就是一个 C# 类,它通过修饰源代码来提供元信息.属性使 C# 能够支 ...