CSP-201312-4有趣的数
问题描述
我们把一个数称为有趣的,当且仅当: 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
代码展示
#include<iostream> #include<vector> using namespace std; int main() { int n; cin >> n; unsigned int** a = new unsigned int* [n + 1];//关键点在于要用无符号的int,这样取余才不会得到负数 for (int i = 0; i < n + 1; i++) { a[i] = new unsigned int[9]; } for (int i = 0; i < n + 1; i++) { for (int j = 0; j < 9; j++) { a[i][j] = 0;//全部初始化为0 } } //求出所有的第三位,进而推出第2位 //第1位一定是2 a[1][5] = 1; a[1][7] = 1; a[1][8] = 2; for (int i = 2; i <= n - 2; i++) { a[i][0] = (a[i - 1][1] + a[i - 1][3])%1000000007;//每次都进行取余操作,避免超出范围 a[i][1] = (a[i - 1][2] + a[i - 1][1] + a[i - 1][4])%1000000007; a[i][2] = (a[i - 1][2] + a[i - 1][5])%1000000007; a[i][3] = (a[i - 1][4] + a[i - 1][6] + a[i - 1][3])%1000000007; a[i][4] = (a[i - 1][5] + a[i - 1][4] + a[i - 1][7] + a[i - 1][4])%1000000007; a[i][5] = (a[i - 1][5] + a[i - 1][8] + a[i - 1][5])%1000000007; a[i][6] = (a[i - 1][7] + a[i - 1][6])%1000000007; a[i][7] = (a[i - 1][8] + a[i - 1][7] + a[i - 1][7])%1000000007; a[i][8] = (a[i - 1][8] + a[i - 1][8])%1000000007; } a[n - 1][1] = a[n - 2][2] + a[n - 2][1] + a[n - 2][4]; cout << (a[n - 1][1])%1000000007 << endl; return 0; }
思路分析:本题最开始比较容易想到的是回溯求解,但是回溯思想在n=30时,所消耗时间就已经非常大,当n=1000时,程序可能在很长时间都计算不出结果,因此要舍弃回溯思想。
之后想到采用动态规划可能会更快,
但动态规划求解的难点在于如何确定状态,
以及求解状态间的转移方程。仔细观察后,每一位的状态均以0,1,2,3是否存在所决定,一共是16种情况,但要去掉7种不符合的情况。在由倒数第i位,进而求解倒数第i+1位时,
第i+1位可依次选择0,1,2,3去除不符合条件的情况,并且选择的位置一定会影响倒数第i位的状态,进而求出由第i+1位的某个状态,选择0,1,2,3中的一个值,所能到达的倒数第i位的状态。