有趣的数

有趣的数

问题描述

我们把一个数称为有趣的,当且仅当:

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

动态规划

刚看到题的时候并没有什么想法,反正时间多,先暴力跑一下看看有没有规律= =

暴力代码:

 1 #include<cstdio>
 2 #include<cstring>
 3 #define LL long long
 4 using namespace std;
 5 char s[1005];
 6 LL sum;
 7 int main(void){
 8     int n;
 9     scanf("%d",&n);
10     LL a=1,b=3;
11     for(int i=1;i<n;++i){
12         a*=10;
13         b=b*10+3;
14     }
15     while(a<=b){
16         LL t=a;
17         memset(s,0,sizeof(s));
18         int k=0;
19         while(t){
20             s[k++]=t%10+'0';
21             t/=10;
22         }
23         bool flag=1;
24         int l1=-1,l2=-1,l3=-1,l0=-1;
25         for(int i=0;i<=k;++i){
26             if(s[i]>'3'){
27                 flag=0;
28                 break;
29             }
30             if(s[i]=='0'){
31                 l0=i;
32             }else if(s[i]=='1'){
33                 if(l0!=-1){
34                     flag=0;
35                     break;
36                 }
37                 l1=i;
38             }else if(s[i]=='2'){
39                 l2=i;
40             }else if(s[i]=='3'){
41                 if(l2!=-1){
42                     flag=0;
43                     break;
44                 }
45                 l3=i;
46             }
47         }
48         if(flag){
49             if(l0!=-1&&l1!=-1&&l2!=-1&&l3!=-1){
50                 sum++;
51             }
52         }
53         a++;
54     }
55     printf("%lld
",sum);
56     return 0;
57 }
View Code

试了几个数据,尼玛n=9的时候就跑不动了QAQ,没找到什么规律,开始想其他解法。

考虑到01串与23串的取法分别独立,当01的总位数为a时,有a-1种方案,12的总位数为b时,有b-1种方案。而题目中所要求的也就可以转变为将a位的01插入到b位的23中共有几种插法,也就是a个球放到b个箱子有几种方案。定义状态dp[i,j]为i个球放j个盒子的方案数,然后有dp[i,j]=1+dp[1,j-1]+dp[2,j-1]+dp[3,j-1]+...+dp[i,j-1];但是如此的话复杂度为O(n^3),对于数据规模10^3肯定会爆...

O(n^3)代码:

 1 #include<cstdio>
 2 #include<cstring>
 3 #define LL long long
 4 #define N 1000
 5 #define Mod 1000000007
 6 using namespace std;
 7 LL dp[N][N];
 8 int main(void){
 9     for(int i=1;i<N;++i){
10         dp[1][i]=i;
11         dp[i][1]=1;
12     }
13     for(int i=2;i<N;++i){
14         for(int j=2;j<N;++j){
15             LL t=1;
16             for(int k=1;k<=i;++k){
17                 t=(t+dp[k][j-1])%Mod;
18             }
19             dp[i][j]=t;
20         }
21     }
22 
23     int n;
24     scanf("%d",&n);
25     LL sum=0;
26     for(int i=2;i<=n-2;++i){
27         LL t=0;
28         t=((((i-1)*(n-i-1))%Mod)*dp[i][n-i])%Mod;
29         sum=(sum+t)%Mod;
30     }
31     printf("%lld
",sum);
32     return 0;
33 }
View Code

纠结了一番发现dp[i,j]=dp[i-1,j]+dp[i,j-1]这样一来复杂度就变为了O(n^2),对于n<1000的数据就轻松过了hhh.

代码如下:

 1 #include<cstdio>
 2 #include<cstring>
 3 #define LL long long
 4 #define N 1000
 5 #define Mod 1000000007
 6 using namespace std;
 7 LL dp[N][N];
 8 int main(void){
 9     for(int i=1;i<N;++i){
10         dp[1][i]=i;
11         dp[i][1]=1;
12     }
13     for(int i=2;i<N;++i){
14         for(int j=2;j<N;++j){
15             dp[i][j]=(dp[i-1][j]+dp[i][j-1])%Mod;
16         }
17     }
18 
19     int n;
20     scanf("%d",&n);
21     LL sum=0;
22     for(int i=2;i<=n-2;++i){
23         LL t=0;
24         t=((((i-1)*(n-i-1))%Mod)*dp[i][n-i])%Mod;
25         sum=(sum+t)%Mod;
26     }
27     printf("%lld
",sum);
28     return 0;
29 }
原文地址:https://www.cnblogs.com/barrier/p/5612528.html