问题描述
我们把一个数称为有趣的,当且仅当:
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
分析:
题目相当于是求由0,1,2,3组成的所有满足一定要求的排列数的 数目。
我的第一想法就是dfs+剪枝。后来分析了一下就知道超时了。因为结果很大,我们再怎么剪枝,也需要将所有的结果找出来一遍,而结果最大值会大于所给的求模的值,所以一定超时。
所以没法往找出所有的结果的方向思考了。那就只有通过推导出他们之间的关系和推导公式来解决问题了。所以就只能向dp思考了。
对于一个给定的合格的数,如2013这个四位数,要是再往后扩展一位,会是什么样的结果呢?
并且我们可以很快分析出,第一个数只能是2
我们将0,1,2,3所有的满足条件2,3的情况进行归纳总结:
(1):所有的数都出现过一次:
5--全部用了
(2):有一个数没有用:只能是1或3没有用,因为若0没有用,那说明1也一定还没有用,不然后面无法出现0了,对于2同理可得,因为若2没有用,那说明3也一定还没有用
4--用了0,2,3,剩1
3--用了0,1,2,剩3
(3)**有两个数没有用:**0,1或者1,3没有用,同理由于条件2,使得其他情况不存在满足的。
2--用了2,3,剩0,1
1--用了0,2,剩1,3
(4)有三个数没有用:只能是0,1,3
0--用了2,剩0,1,3
我们用dp[i][0] ~ dp[i][5]依次表示为当长度为i时,各种情况的组合的数目。
所以递推公式为:
dp[i][0] = 1;
当只用了2时,只用一种情况2..222
dp[i][1] = (dp[i-1][0] + dp[i-1][1]*2) % mod;
在i-1位的条件下,dp[i-1][0]只用了2,所以在第i位为:0
在i-1位的条件下,dp[i-1][1]只用了0,2,所以在第i位为:0或2
dp[i][2] = (dp[i-1][0] + dp[i-1][2]) % mod;
在i-1位的条件下,dp[i-1][0]只用了2,所以在第i位为:3
在i-1位的条件下,dp[i-1][2]只用了2,3,所以在第i位为:3(2在3的前面)
dp[i][3] = (dp[i-1][1] + dp[i-1][3]*2) % mod;
在i-1位的条件下,dp[i-1][1]只用了0,2,所以在第i位为:1
在i-1位的条件下,dp[i-1][3]只用了0,1,2,所以在第i位为:1或2
dp[i][4] = (dp[i-1][1] + dp[i-1][2] + dp[i-1][4]*2) % mod;
在i-1位的条件下,dp[i-1][1]只用了0,2,所以在第i位为:3
在i-1位的条件下,dp[i-1][2]只用了2,3,所以在第i位为:0
在i-1位的条件下,dp[i-1][4]只用了0,2,3,所以在第i位为:0或3
dp[i][5] = (dp[i-1][3] + dp[i-1][4] + dp[i-1][5]*2) % mod;
在i-1位的条件下,dp[i-1][3]只用了0,1,2,所以在第i位为:3
在i-1位的条件下,dp[i-1][4]只用了0,2,3,所以在第i位为:1
在i-1位的条件下,dp[i-1][5]全用了,所以在第i位为:1或3
AC代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <map>
using namespace std;
const int maxn = 1010;
const long long mod = 1000000007;
long long dp[maxn][10];
void init()
{
for(int i=0;i<6;i++) dp[0][i] = 0;
for(int i=1;i<maxn;i++)
{
dp[i][0] = 1;
dp[i][1] = (dp[i-1][0] + dp[i-1][1]*2) % mod;
dp[i][2] = (dp[i-1][0] + dp[i-1][2]) % mod;
dp[i][3] = (dp[i-1][1] + dp[i-1][3]*2) % mod;
dp[i][4] = (dp[i-1][1] + dp[i-1][2] + dp[i-1][4]*2) % mod;
dp[i][5] = (dp[i-1][3] + dp[i-1][4] + dp[i-1][5]*2) % mod;
}
}
int main()
{
//freopen("in.txt","r",stdin);
int n;
init();
scanf("%d",&n);
printf("%d\n",dp[n][5]);
return 0;
}