代码倒是不难,就是有点费博主。
——题记
众所周知,动态规划往往是信竞人学习新课路上的最后一课。对于我个人而言,动态规划确实是不简单的一关。DP,难就难在它的灵活性,要求我们具体问题具体分析。
对,就是语文老师口中的那种具体问题具体分析。
DP种类中,相对简单的就是背包DP,在这之后就是最长的不下降子序列和不上升子序列。他们简单,主要是因为其中有基本思路甚至是模板。
目前来看,最有难度的是现在博主正在研究的线性DP(虽然子序列问题属于线性DP,但是除了O(n2)的时间复杂度和双重循环我实在没发现什么共同点)这样的动态规划中,寻找DP数组中的各项参量的合理位置是一个难点。并且动态规划,我们还要想明白从什么样的基本状态开始,怎样方便输出。所以一般来说,输出的答案是存储在数组中的数据。
好了,上题
题目
考虑一个由N个整数构成的数列,其中1到N都在数列中出现了恰好一次。
•在这个数列中从左到右任取两个数,如果前者比后者大,那么这对数就是一个逆序对。而整个数列的逆序数就是其中所有逆序对的总数。
例如,数列(1,4,3,2)的逆序数为3,因为存在三个逆序对:(4,3),(4,2)和(3,2)。
•写一个程序,计算有多少长度为N的这种数列,使它的逆序数恰为C。
输入
Input contains 2 natrual numbers:N (N<=1000)ans C(<=10000).
输出
•计算出所求的答案,将它模1 000 000 007后输出。
示例输入、输出
输入1
10 1
输出1
9
输入2
4 3
输出2
6
输入3
9 13
输出3
17957
思路
对于这种题来说,我们上来要思考的并不是怎样DP的问题,而是这里面的数学关系。经过博主一个上午的思考,得出以下结论:
对于一个有n个数字组成,逆序对数量为c的1-n组成数列来说,我们一般会采用一种复杂度为O(n2)的计数方法来数它的逆序对数量。以某一个元素为标杆,和它后面的全部数据进行比较,找出逆序对。我的灵感就来源于此。
现在,请大家思考一个问题:
如果我们再多加上一个元素“n+1”并且想要保持这个逆序对数量不变的话,我们有几种方案?
从这个新元素n+1的位置上来考虑的话,有n种大情况,而在这样的n种大情况之下,如果我们要保证逆序对的数量为c的话,我们依然可以构建很多个符合条件的数组。根据题目要求,我们可以保证这个新元素是数列中最大的元素,那么,设该元素在第a个位置上的话,以它为较大值的逆序对数量,也就是由于这个数的加入而额外产生的逆序对的数量就是n-a个。
然后呢?
我们需要求出n个元素中拥有c-(n-a)个逆序对的数列的数量。这就是DP中的一个很重要的数据关系。我们将新元素的位置分别放在n+1个位置上,多构成n-a(a还是最大数的位置)个逆数对。然后利用求出的c-(n-a)个逆序对的数列的数量,∑,就求出了一个新答案。
如此一来就能够算出答案啦!
于是,我心血来潮写出了这个代码👇
TLE代码
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int dp[1001][10001];
int n , c