題目:
1. 它的數字只包含0, 1, 2, 3,且這四個數字都出現過至少一次。
2. 所有的0都出現在所有的1之前,而所有的2都出現在所有的3之前。
3. 最高位數字不爲0。
因此,符合我們定義的最小的有趣的數是2013。除此以外,4位的有趣的數還有兩個:2031和2301。
請計算恰好有n位的有趣的數的個數。由於答案可能非常大,只需要輸出答案除以1000000007的餘數。
思路:
在瀏覽了很多大神的題解後終於明白了。
數位DP;
在本題中首先定義狀態dp[i][j],記爲長度爲i的數字串中,狀態爲j的情況個數
本題中可以分析出6中狀態:
0. 在前n位數中只有一種數字且爲2
1. 在前n位數中有兩種數字爲 2,0
2. 在前n爲數中有兩種數字爲 2,3
3. 在前n位數中有三種數字爲 2,0,1
4. 在前n位數中有三種數字爲 2,0,3
5. 在前n位數中有四種數字爲 2,0,1,3
然後就是狀態轉移方程:
對於每種狀態dp[i][j],其可由長度爲i-1的數字串在後面加上一種數字得到
即dp[i][j] = ∑(dp[i-1][可以通過加上某種數字得到第j種狀態的狀態]);
複雜度:
時間複雜度:O(N)
空間複雜度:O(N)
#include<iostream>
#include<cstring>
#define MAX 1007
using namespace std;
typedef long long ll;
const ll mod=1000000007;
ll dp[MAX][6]; // dp[i][j]表示i位數爲第j種狀態的種數
int main(){
int n;
cin>>n;
/*
數位DP
本題共有6中狀態
0. 在前n位數中只有一種數字且爲2
1. 在前n位數中有兩種數字爲 2,0
2. 在前n爲數中有兩種數字爲 2,3
3. 在前n位數中有三種數字爲 2,0,1
4. 在前n位數中有三種數字爲 2,0,3
5. 在前n位數中有四種數字爲 2,0,1,3
*/
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;++i){
//第0種狀態,由於只有2,所以始終只有一種情況
dp[i][0] = 1;
//第1種狀態,由於爲2,0,可以在dp[i-1][1]後加0或2,或者在dp[i-1][0]後面加上0
dp[i][1] = (dp[i-1][1]*2 + dp[i-1][0])%mod;
//第2種狀態,由於爲2,3,可以在dp[i-1][1]後加3(不能加2,2必須在3前),或者在dp[i-1][0]後加3
dp[i][2] = (dp[i-1][2] + dp[i-1][0])%mod;
//第3種狀態,由於爲2,0,1,可以在dp[i-1][3]後加2,1(不能加0,0必須在1前),或者在dp[i-1][1]後加1
dp[i][3] = (dp[i-1][3]*2 + dp[i-1][1])%mod;
//第4種狀態,由於爲2,0,3,可以在dp[i-1][4]後加0,3,或者在dp[i-1][1]後加3,或者在dp[i-1][2]後加0
dp[i][4] = (dp[i-1][4]*2 + dp[i-1][1] + dp[i-1][2])%mod;
//第5種狀態,由於爲2,0,1,3,可以在dp[i-1][5]後加1,3,或者在dp[i-1][3]後加3,或者在dp[i-1][4]後加1
dp[i][5] = (dp[i-1][5]*2 + dp[i-1][3] + dp[i-1][4])%mod;
}
cout<<dp[n][5]<<endl;
return 0;
}