思想解释参考自CSDN博客,写的比较乱我整理了一下。
首位即为左边第一位,从左到右位数依次递加。
应用动态规划的思想,核心公式为:
F[m][0]=F[m-1][0]=...=F[1][0]=1
F[m][1]=F[m-1][0]+F[m-1][1]×2
F[m][3]=F[m-1][1]+F[m-1][3]×2
F[m][4]=F[m-1][1]+F[m-1][2]+F[m-1][4]×2
F[m][5]=F[m-1][3]+F[m-1][4]+F[m-1][5]×2
F[0][0] = 0
F[0][1] = 0
F[0][2] = 0
F[0][3] = 0
F[0][4] = 0
F[0][5] = 0
解释如下:
一共有如下6种状态:
状态 | 0 | 1 | 2 | 3 |
0 | √ | |||
1 | √ | √ | ||
2 | √ | √ | ||
3 | √ | √ | √ | |
4 | √ | √ | √ | |
5 | √ | √ | √ | √ |
带√表示状态n包含对应的数字。
状态转移效果图如下
F[m][n]的表示,第m位添加一位数字转移到状态n,到达当前状态的数字个数。
注意状态5为题设中的要求状态,即0123四个数字都存在,状态01234均是到达状态5的中间步骤
状态0~5中的数字表示进行至当前状态,形成的数字中只含有状态中包含的数字
状态0:数字首位只能放置2(0不能放在首位;首位放置1,3后面就不能放置0或2了,最终达不到0123都有的状态)
在该状态可在后一位添加2,数字位数+1,状态不变,公式化表示即
F[m][0]=F[m-1][0]=...=F[1][0]=1
状态1:状态1有两种形成方式
(1)由状态0向后添加0转移得到
(2)由状态1在末尾添加0或2转移到自身(两种途径因此乘2):前面没有1,3所以后面随便摆放0,2
公式化表示为:
F[m][1]=F[m-1][0]+F[m-1][1]×2
状态2:有两种形成方式
(1)状态0后加3转移得到
(2)自身末尾加3转移得到:前面放几个2可以在状态0循环获得,末尾不能加2了,因为2必须在3前面
公式化表示为:
F[m][2]=F[m-1][0]+F[m-1][2]×2
状态3:有两种形成方式
(1)状态2填1转移得到
(2)状态3本身末尾添加1或2转移到自身,因为前方有1所以后面不能放0了
公式化表示为:
F[m][3]=F[m-1][1]+F[m-1][3]×2
状态4:有三种形成方式
(1)状态1填3转移得到
(2)状态2填0转移得到
(3)状态4末尾添加0或3,因为前方有3所以后面不能放2
公式化表示为:
F[m][4]=F[m-1][1]+F[m-1][2]+F[m-1][4]×2
状态5:有三种形成方式
(1)状态3填3转移得到
(2)状态4填1转移得到
(3)状态5末尾添加1或3,因为前方有1,3所以后面不能放0,2
公式化表示为:
F[m][5]=F[m-1][3]+F[m-1][4]+F[m-1][5]×2
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const long long MOD = 1000000007;
const int MAX_N = 1001;
int main(){
int n;//输入数字的位数
long long data[MAX_N][6];
cin>>n;
memset(data,0,sizeof(data));
for(int i = 1; i <= n; i++){
data[i][0] = 1;
data[i][1] = (data[i-1][0] + data[i-1][1]*2)%MOD;
data[i][2] = (data[i-1][0] + data[i-1][2])%MOD;
data[i][3] = (data[i-1][1] + data[i-1][3]*2)%MOD;
data[i][4] = (data[i-1][1] + data[i-1][2] + data[i-1][4]*2)%MOD;
data[i][5] = (data[i-1][3] + data[i-1][4] + data[i-1][5]*2)%MOD;
}
cout<<data[n][5]<<endl;
return 0;
}