题目
题目描述: 小美有一个由n个元素组成的序列{a1,a2,a3,...,an},她想知道其中有多少个子序列{ap1,ap2,...,apm}(1 ≤ m ≤ n, 1 ≤ p1 < p2 ,..., < pm ≤ n),满足对于所有的i,j(1 ≤ i < j ≤ m), apipj < apjpi成立。
第一行一个整数n (1≤n≤100)表示序列长度。
接下来一行n个整数{a1,a2,a3,...,an}(1≤ai≤100)表示序列。
输出一行表示满足条件的子序列的数目。因为答案可能很大,请输出答案mod 1e9 + 7。
解析
这道题一看就发现是dp的题目,没有像大佬们用了树状数组啥的(我不会)
- dp其实我都不咋会,但是还是不厌其烦的说:动态规划最基本的就是递推。
所以我们只要能得出递推公式,这道题就很简单了。
- 我们这道题根据题目意思就是,两个节点之间需要满足一定的关系:apipj < apjpi,才可计数。
- 从这里我们可以想到节点与节点之间的传递性(不好意思我没想到,感谢邓老师)。
- 我所说的传递性简单来说就是:假如有三个点:a,b,c。如果ab和bc之间同时存在这种关系,ac也存在这种关系。(我们下一点来证明一下)
- 首先根据题目条件:apipj < apjpi。我们在两边取对数可以得到。然后我们就发现移项后得到:。说明单调递增,当然有传递性。
(什么?你问我为什么取对数?别问,问就是经验)(其实还有就是防止数据溢出) - 传递性又能干啥呢?:我们先用一个dp数组保存一定区间内,并包含区间末节点的方法数(例如dp[pos],dp区间为pos之前,并且表示的方法中一定包含pos位置上的节点)。
而从传递性中我们可以发现,每一个满足条件的数据都可以继承与他相关的方法数(例如x与y有关系,y一定包含x的所有方法数:x所有的方法加一个y没有影响)。 - 最后对每个位置上的求和就是方法总数。详情看代码趴~~~
AC代码
#include <iostream> #include <algorithm> #include <cmath> using namespace std; //代码预处理区 const int MOD = 1e9 + 7; const int MAX = 1e2 + 7; //全局变量区 template<class T>inline void read(T& res);//整型快读函数 //函数预定义区 int main() { int n; read(n); int a[MAX], dp[MAX]; for (int i = 1; i <= n; i++) read(a[i]); //数组输入 fill(dp + 1, dp + 1 + n, 1); for (int right = 1; right <= n; right++) for (int left = 1; left < right; left++) if (right * log(a[left]) < left * log(a[right])) dp[right] = (dp[right] + dp[left]) % MOD; //根据递推公式计算 int sum = 0; for (int i = 1; i <= n; i++) sum = (sum + dp[i]) % MOD; //答案为方案总数:求和 printf("%d", sum); return 0; } template<class T>inline void read(T& res) { char c; T flag = 1; while ((c = getchar()) < '0' || c > '9') if (c == '-') flag = -1; res = c - '0'; while ((c = getchar()) >= '0' && c <= '9') res = res * 10 + c - '0'; res *= flag; } //函数区