数学已不易,信息坑更深——DP解决[COCI2006#4] ZBRKA

代码倒是不难,就是有点费博主。
——题记

众所周知,动态规划往往是信竞人学习新课路上的最后一课。对于我个人而言,动态规划确实是不简单的一关。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
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值