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位的状态。

posted @ 2022-03-17 18:44  不太聪明的小高  阅读(58)  评论(0)    收藏  举报