CCF認證——有趣的數(數位DP)
阿新 • • 發佈:2019-02-10
題目:
問題描述 我們把一個數稱為有趣的,當且僅當: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 題目來源:
思路:
在瀏覽了很多大神的題解後終於明白了。
數位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; }