试题编号: | 201312-4 |
试题名称: | 有趣的数 |
时间限制: | 1.0s |
内存限制: | 256.0MB |
问题描述: |
问题描述
我们把一个数称为有趣的,当且仅当:
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
|
第一眼看还以为挺简单,再一想想这量级。。。肯定不能用暴力的方法了。
不过,还是有个暴力的代码,超时了,得了20分。
#include<iostream>
using namespace std;
int num[1005]={};
int count=0;
void gooneplace(int place){
if(place<count){
if(num[place]>=4){
if(place<count-1){
num[place]=0;
num[place+1]++;
gooneplace(place+1);
}
}
}
}
bool checkall(){
int a=0,b=0,c=0,d=0;
for(int i=1;i<=count;i++){
if(num[i]==0){
a=1;
}
if(num[i]==1){
b=1;
}
if(num[i]==2){
c=1;
}
if(num[i]==3){
d=1;
}
}
a=a*b*c*d;
if(a>0){
return true;
}
return false;
}
bool check01(){
int point=0;
for(int i=1;i<=count;i++){
if(num[i]==0&&point==0){
point=1;
}
if(num[i]==1&&point==1){
point=-1;
}
}
if(point==1){
return true;
}
if(point==-1){
return false;
}
return false;
}
bool check23(){
int point=0;
for(int i=1;i<=count;i++){
if(num[i]==2&&point==0){
point=1;
}
if(num[i]==3&&point==1){
point=-1;
}
}
if(point==1){
return true;
}
if(point==-1){
return false;
}
return false;
}
int main(){
cin>>count;
num[count]=2;
int cont=0;
while(num[count-1]<4){
gooneplace(1);
if(checkall()){
if(check01()){
if(check23()){
cont++;
}
}
}
num[1]++;
}
cout<<cont;
}
大家看看就好,不要和我一样无聊。
————————————————————————————————————————————————————
最开始想了一种方法,比方说:
两串数000....00111....11和222....22333....33。
n个0,m个1;x个2,y个3,然后n+m+x+y-1个位置,计算情况。
本来我以为这是高中的题目,只是我忘了怎么做,结果问数学院的同学,她直接不想帮我算T_ T。
刚刚看到一个用这个方法做的
链接:http://blog.csdn.net/u011589125/article/details/51058777
————————————————————————————————————————————————————
于是看了看别人的题目分析,用到状态这一抽象名词。
我理解了一下,这个大概是和我们写VHDL时候,定义的状态是相同的。
这里可以这样分析:
先是最高位,只能是2
2——0 : 后面还可以接1或3
0——0 : 延续当前情况
——1 : 后面还可以接3
——2 : 延续当前情况
——3 : 后面还可以接1
——1 : 不能成立,1不能在0前面 pass
——2 : 延续当前情况
——3 : 后面还可以接0或1
3——0 : 后面还可以接1
——1 : 不能成立,1不能在0前面 pass
——2 : 不能成立,2不能在3前面 pass
——3 : 延续当前情况
.................................................................................
继续分析归纳出六种状态
注:这六种状态归纳出来后面接的意思是可以改变当前状态的接法
即接当前状态不存在的数。
0 状态——0 1 3
1 状态——1 3
2 状态——0 1
3 状态——3
4 状态——1
5 状态——无
因此,反向得出下一状态由哪些上一状态组成。
当前状态0 <—— A.上一状态0接2;
当前状态1 <—— A.上一状态0接0;B.上一状态1接2或0;
当前状态2 <—— A.上一状态0接3;B.上一状态2接3;
当前状态3 <—— A.上一状态2接1;B.上一状态3接1或2;
当前状态4 <—— A.上一状态1接3;B.上一状态2接0;C.上一状态4接0或3;
当前状态5 <—— A.上一状态3接3;B.上一状态4接1;C.上一状态5接1或3;
最后直接输出最后的状态5就可以了
代码如下:
(吸取了前人经验,long long 定义变量)
#include<iostream>
using namespace std;
const long long mod=1000000007;
long long state[6][1005];
int main(){
long long n;
cin>>n;
for(int i=0;i<6;i++){
state[i][1]=0;
}
state[0][1]=1;
for(long long i=2;i<n+1;i++){
state[0][i]=(state[0][i-1])%mod;
state[1][i]=(state[0][i-1]+state[1][i-1]*2)%mod;
state[2][i]=(state[0][i-1]+state[2][i-1])%mod;
state[3][i]=(state[1][i-1]+state[3][i-1]*2)%mod;
state[4][i]=(state[1][i-1]+state[2][i-1]+state[4][i-1]*2)%mod;
state[5][i]=(state[3][i-1]+state[4][i-1]+state[5][i-1]*2)%mod;
}
cout<<state[5][n];
return 0;
}