动态规划的题目一般都是先找到变化的量,再找到状态转移方程,最后建立一个dp表即可。但对于动态规划的难点就是如何寻找状态转移方程。下面我会很详细的一步步的说明如何找到状态转移方程。
问题描述
如果一个序列满足下面的性质,我们就将它称为摆动序列:
1. 序列中的所有数都是不大于k的正整数;
2. 序列中至少有两个数。
3. 序列中的数两两不相等;
4. 如果第i – 1个数比第i – 2个数大,则第i个数比第i – 2个数小;如果第i – 1个数比第i – 2个数小,则第i个数比第i – 2个数大。
比如,当k = 3时,有下面几个这样的序列:
1 2
1 3
2 1
2 1 3
2 3
2 3 1
3 1
3 2
一共有8种,给定k,请求出满足上面要求的序列的个数。
输入格式
输入包含了一个整数k。(k<=20)
输出格式
输出一个整数,表示满足要求的序列个数。
首先,要把这道题的题目条件读懂,前三点都很容易理解,关键点是最后一点,看起来很绕,其实稍微理解一下,它说的就是假如有三个数,由题目3的条件可知它们都是大小不同的,也就是把这三个数比作 大 中 小 。将它们进行排列,只有(中 小 大)和(中 大 小)这两种情况满足要求,换句话说大的那个数和小的那个数顺序随意,但是中间大小的那个数字一定要在最右边。
刚刚我们讲的是3个数的情况,推广到多个数字的时候,就是其中的每三个数都满足上述情况的排列,才算是一种合格的排列。
读完了题目后,我们就要开始找变化的量,首先k肯定是一个变化的量毋庸置疑。但动态规划一般都有两个变化的量,观察题目第一点,我们发现最大的数字也在变化。既然这样我们就先建立一张二维表,横坐标为这个序列的长度,纵坐标为这个序列的最大值。
len=2 | len=3 | len=4 | len=5 | |
---|---|---|---|---|
k=2 | ||||
k=3 | ||||
k=4 | ||||
k=5 | ||||
这样一张dp表就建立好了。下面我们就要试着往dp表里填一些东西来试图找到某种转移的规律。首先当k为2的情况,自然是只有1、2和2、1两种情况,当k为3时题目已经给出样例,我们不妨先填进去。 | ||||
len=2 | len=3 | len=4 | len=5 | |
– | – | – | – | – |
k=2 | 2 | 0 | 0 | 0 |
k=3 | 6 | 2 | 0 | 0 |
k=4 | ||||
k=5 | ||||
但这样好像还不够,这张表里有用的数据还是太少了,我们很难通过它来找到规律,所以我们可以试着把k=4的情况算手动算出来。 | ||||
len=2 | len=3 | len=4 | len=5 | |
– | – | – | – | – |
k=2 | 2 | 0 | 0 | 0 |
k=3 | 6 | 2 | 0 | 0 |
k=4 | 12 | 8 | 2 | 0 |
k=5 | ||||
以上的工作都是准备工作,通过这些准备工作,我们能够得到一张比较全面的dp表,这样做有利我们寻找规律。现在我们通过这张构建好的表来看看,首先看第一列,2,6,12这几个数字好像有点规律,但我们还是要结合题意来思考,长度为2的时候,最大值为2的时候,有(1,2)、(2,1)两种情况。当长度为2的时候,最大值为3的时候有(1,2)、(1,3)、(2,1)、(2,3)、(3,1)、(3,2)6种情况,由此我们好像发现,当长度为2的时候,就是1-k中任意挑两个数字进行排列,为了确保我们的猜想是对的,我们可以算k=4时, C 4 2 C_4^2 C42 等于12,和表中的数据对应上了。 | ||||
但是,原因是什么呢? 其实原因很简单,只有2个数字的情况对于题目要求的第四点是都成立的,所以就成了一个排列问题。dp表中的第一列都会等于 C k 2 C_k^2 Ck2 | ||||
迄今为止,我们只差把中间部分的递推方程给解出来即可,我们先观察k=4,len=3情况。因为它周围都有数据,我们把它情况列出来, | ||||
(2,3,1)(2,1,3)(2,1,4)(2,4,1) | ||||
(3,1,4)(3,4,1)(3,2,4)(3,4,2) | ||||
共8种排列,而对于它上面一列k=3,len=3来说,上面一列肯定是下面一列的子集,因为最大值增加了,排列肯定会包括原来的那部分,所以dp方程为 | ||||
dp[i][j]=dp[i-1][j]+一个数字 | ||||
如果对数据比较敏感的或者比较有经验的人肯定已经发现了这是数就是 | ||||
dp[i-1][j-1] 。 但原因是什么呢? | ||||
原因就是我们dp表中的i行j列其实就相当于在i-1行j-1列中插入一个k,以k=3,len=3举例,它就相当于在(1,2)和(2,1)排列里面插入3这个数字,插入后要满足题目第四点给出的情况,我们只能是在(2,1)里面插,因为在(1,2)里面插无论如何也不能满足中间的数在最左边,在这里这个中间数是2,无论是插在左边(3,1,2)还是插在中间(1,3,2)还是插在右边(1,2,3)都不满足要求。而对于(2,1)来说只能插在最右边和中间,因为K是最大的数字,K不能在左边。所以有两种插入情况。 | ||||
推广到更大的len来说,有一半的排列是不能插的,而对于另一半来说,这个最大的数可以插在最右边和倒数第二个位置。如果再往左边的话,就会不满足题目要求了。所以相当于(i-1)(j-1)的情况除2再乘2等于本身。得出最后的递推方程: | ||||
dp[i][j]=dp[i-1][j-1]+dp[i-1][j] |
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int[][] dp=new int[n+1][n+1];
for(int i=2;i<=n;i++) {
dp[i][2]=i*(i-1);
}
for(int i=2;i<=n;i++) {
for(int j=3;j<=n;j++) {
dp[i][j]=dp[i-1][j-1]+dp[i-1][j];
}
}
int sum=0;
for(int i=2;i<=n;i++)
sum+=dp[n][i];
System.out.println(sum);
}
}