CCF 201312-4 有趣的数

问题描述

  我们把一个数称为有趣的,当且仅当:
  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为长度为2S1数,那么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为长度为2S1数,那么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为长度为2S2数,那么201为长度为3的S4

同理,对于S5S6,可以得到相应的递推公式。详细参见以下的源程序。

#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;
}

 

posted @ 2020-02-06 20:38  神犇(shenben)  阅读(222)  评论(0编辑  收藏  举报