问题描述
我们把一个数称为有趣的,当且仅当:
1. 它的数字只包含0, 1, 2, 3,且这四个数字都出现过至少一次。
2. 所有的0都出现在所有的1之前,而所有的2都出现在所有的3之前。
3. 最高位数字不为0。
因此,符合我们定义的最小的有趣的数是2013。除此以外,4位的有趣的数还有两个:2031和2301。
请计算恰好有n位的有趣的数的个数。由于答案可能非常大,只需要输出答案除以1000000007的余数。
输入格式
输入只有一行,包括恰好一个正整数n (4 ≤ n ≤ 1000)。
输出格式
输出只有一行,包括恰好n 位的整数中有趣的数的个数除以1000000007的余数。
样例输入
4
样例输出
3
题解
dp:对一个n位数的解都基于前一个n-1位的数的最优解。
我们对一个数的第n位规定一个状态集:即到这一位为止还有几个数字没有使用(我们有0123共四个数)。
对第n位而言,经过归纳,有六种状态:
状态 | 已用 | 剩余 |
0 | 2 | 0 1 3 |
1 | 2 0 | 1 3 |
2 | 2 3 | 0 1 |
3 | 2 0 1 | 3 |
4 | 2 0 3 | 1 |
5 | 2 0 1 3 |
接着,我们需要分析,每个状态可以通过哪些状态转移得到:
状态0:只有一种可能,即状态0本身;
状态1:①第n-1位是状态0的情况下,第n位填0(以下略写);②状态1填2或0(2与0之间并无冲突,故在第n-1位已达到状态1的情况下,在第n位填2或0都是合法的,由于有两种可能,所以需要将在状态1情况下的第n-1位的可能数乘以2);
状态2:①状态0填3;②状态2填3(由于2与3有冲突,且3必须出现在2的后面,所以在2、3已经出现的情况下,只能在第n位填3,否则违反了规则);
状态3:①状态1填1;②状态3填2或1,故乘以2;
状态4:①状态1填3;②状态2填0;③状态4填0或3,故乘以2;
状态5:①状态3填3;②状态4填1;③状态5填1或3,故乘以2;
赘述:
1.只包含数字0、1、2和3
2.0、1、2和3各自至少出现一次
3.所有的0都出现在1之前
4.所有的2都出现在3之前
5.最高位数字不为0
数(字符串)可以分为以下六种情况(或称为状态):
1.只包含数字2,记为S1
2.只包含数字2和0(0开始的数0个,以此数为前缀的数均不是以0开始),记为S2
3.只包含数字2和3,记为S3
4.只包含数字2、0和1,并且满足所有0在1之前,记为S4
5.只包含数字2、0和3,并且满足所有2在3之前,记为S5
6.包含任意数字(包含0、1、2和3),满足所有0在1之前,满足所有2在3之前,记为S6
考虑递推式:
1.对于S1,考虑其长度l,定义f(l,S1)为长度l的S1数的数量,则f(l,S1)=1。也就是说长度为l的只包含2的数只有1个。
2.对于S2,考虑其长度l,定义f(l,S2)为长度l的S2数的数量,当l=1则那么f(l,S2)=0,即f(1,S2)=0,当l>1则f(l,S2)=f(l-1,S2)*2+f(l-1,S1)。这是因为,长度为l的S2数可以是由长度为l-1的S2的数加上2或0构成,例20为长度为2的S2数,那么202和200为长度为3的S2数;另外,长度为l的S2数可以是由长度为l-1的S1的数加上0构成,例如22为长度为2的S1数,那么220为长度为3的S2数。
3.对于S3,考虑其长度l,定义f(l,S3)为长度l的S3数的数量,当l=1则那么f(l,S3)=0,即那么f(1,S3)=0,当l>1则f(l,S3)=f(l-1,S3)*2+f(l-1,S1)。这是因为,长度为l的S3数可以是由长度为l-1的S3的数加上2或3构成,例23为长度为2的S3数,那么232和233为长度为3的S3数;另外,长度为l的S3数可以是由长度为l-1的S1的数加上3构成,例如22为长度为2的S1数,那么223为长度为3的S3数。
4.对于S4,考虑其长度l,定义f(l,S4)为长度l的S4数的数量,当l=1则那么f(l,S4)=0,即f(1,S4)=0,当l>1则f(l,S4)=f(l-1,S4)*2+f(l-1,S2)+f(l-1,S3)。这是因为,长度为l的S4数可以是由长度为l-1的S4的数加上2或1构成(满足所有0在1之前,不可以加上0),例201为长度为3的S4数,那么2012和2011为长度为4的S4数;另外,长度为l的S4数可以是由长度为l-1的S2的数加上1构成,例如20为长度为2的S2数,那么201为长度为3的S4数。
同理,对于S5和S6,可以得到相应的递推公式。详细参见以下的源程序。
#include<iostream> using namespace std; const int N=1005,mod=1e9+7; int n;long long f[N][6]; /**6种状态 * 0--剩013 * 1--剩13 * 2--剩01 * 3--剩3 * 4--剩1 出现什么数能满足当前的状态,维持当前的状态。 * 5--无 其实0 1 2 3 4这5种状态都是不满足题意的都是为了 计算出第5种状态长度为n的时候的长度。 **/ int main(){ cin>>n; for(int i=1;i<=n;i++){ f[i][0]=1;//只放2那么肯定只有一种,也就是i个2. f[i][1]=(f[i-1][0]+f[i-1][1]*2)%mod;//状态1可由状态0,在i位放0得到;可由自身状态1,在i位放1或3得到 f[i][2]=(f[i-1][0]+f[i-1][2])%mod;//状态2可由状态0放3得到;可由自身状态放0或1得到 f[i][3]=(f[i-1][1]+f[i-1][3]*2)%mod;//状态3可由状态1,在i位放1得到,也可由自身,放2或者放1. f[i][4]=(f[i-1][1]+f[i-1][2]+f[i-1][4]*2)%mod;//状态4可由状态2,在i位放0得到;可由状态1放3得到,也可由自己放3或者0得到。 f[i][5]=(f[i-1][3]+f[i-1][4]+f[i-1][5]*2)%mod;//状态5可由状态3,在i位放3得到;可由状态4在i位放1得到;可由状态5在i位放1,3得到。 } cout<<f[n][5]; return 0; }