有趣的数
有趣的数
问题描述
我们把一个数称为有趣的,当且仅当:
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
动态规划
刚看到题的时候并没有什么想法,反正时间多,先暴力跑一下看看有没有规律= =
暴力代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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\n",sum); 56 return 0; 57 }
试了几个数据,尼玛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)代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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\n",sum); 32 return 0; 33 }
纠结了一番发现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\n",sum); 28 return 0; 29 }