有趣的数

我们把一个数称为有趣的,当且仅当:

  1. 它的数字只包含 0 , 1 , 2 , 3 0,1,2,3 0,1,2,3,且这四个数字都出现过至少一次。
  2. 所有的 0 0 0 都出现在所有的 1 1 1 之前,而所有的 2 2 2 都出现在所有的 3 3 3 之前。
  3. 最高位数字不为 0 0 0
    因此,符合我们定义的最小的有趣的数是 2013 2013 2013

除此以外, 4 4 4 位的有趣的数还有两个: 2031 2031 2031 2301 2301 2301

请计算恰好有 n n n 位的有趣的数的个数。

由于答案可能非常大,只需要输出答案除以 1 0 9 + 7 10^9+7 109+7 的余数。

输入格式
输入只有一行,包括恰好一个正整数 n n n

输出格式
输出只有一行,包括恰好 n n n 位的整数中有趣的数的个数除以 1 0 9 + 7 10^9+7 109+7 的余数。

数据范围
4 ≤ n ≤ 1000 4≤n≤1000 4n1000
输入样例:

4

输出样例:

3
  1. 思路:动态规划
    假设我们从高位一直到低位对每个位进行赋值,那么我们会发现根据赋值情况,我们可以根据已经使用的数字 ( 0 , 1 , 2 , 3 ) (0,1,2,3) 0123将数据分为 6 6 6种状态:
  • 首先我们从最高位开始,由于 0 , 2 0,2 02分别需在 1 , 3 1,3 13前面而 0 0 0又不能放置在最高位,最高位只能是 2 2 2,因此我们得到第一种状态,即 n − 1 n-1 n1位都是为 2 2 2的情况
  • 现在我们再放置一个数,目前有 0 , 1 , 3 0,1,3 013没用,由于 0 0 0必须在 1 1 1前面,所以我们在放置 1 1 1是必须先放置 0 0 0。此时我们可以放置 0 0 0或者 3 3 3,基于此,我们得到两种状态,一种是前 n − 1 n-1 n1为只有 2 、 0 2、0 20另一种前 n − 1 n-1 n1位只有 2 、 3 2、3 23
  • 现在我们再放置一个数,对于已经放置 2 2 2 0 0 0的状态,我们可以放置 1 1 1 3 3 3,则又得到两种状态,分别是 2 、 0 、 1 2、0、1 201 2 、 0 、 3 2、0、3 203;对于已经放置 2 2 2 3 3 3的状态,我们只能放置 0 0 0 0 0 0必须优先放于 1 1 1前面)。得到状态 2 、 3 、 0 2、3、0 230(此状态和 2 、 0 、 3 2、0、3 203相同)
    4、最后一种所有 0 , 1 , 2 , 3 0,1,2,3 0123都被使用

6种状态如下:
  0--用了2,剩0,1,3
  1--用了0,2,剩1,3
  2--用了2,3,剩0,1
  3--用了0,1,2,剩3
  4--用了0,2,3,剩1
  5--0,1,2,3
后面的状态由前面状态转化而来。如:
5 5 种状态全部使用可以由第 5 5 中状态自身维持或第 3 3 种状态或第 4 4 种状态转化而来
转化如下:假设从 n − 1 n-1 n1 n n n
5 5 中状态自身维持:可以在 n n n位放置 1 1 1 3 3 3(维持自身状态不变只能放置 1 1 1 3 3 3,因为前面已经有 1 , 3 1,3 13所以再放 0 , 2 0,2 02就会违反规则)
3 3 种状态:可以在 n n n位放置 3 3 3
4 4 种状态:可以在 n n n位放置 1 1 1
得到如下公式:
s t a t e s [ i ] [ 5 ] = ( s t a t e s [ j ] [ 3 ] ( 达 到 状 态 五 仅 有 在 1 后 加 3 ) + s t a t e s [ j ] [ 4 ] ] ( 达 到 状 态 五 仅 有 2301 ) + s t a t e s [ j ] [ 5 ] ∗ 2 ( 达 到 状 态 五 仅 有 加 1 / 3 ) states[i][5] = (states[j][3](达到状态五仅有在1后加3) + states[j][4] ](达到状态五仅有2301)+ states[j][5] * 2(达到状态五仅有加1/3) % mod states[i][5]=(states[j][3]13+states[j][4]]2301+states[j][5]21/3);

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int maxn=1010;
const int mod=1e9+7;
ll n;
ll f[maxn][6];

int main() {
    int n;
    scanf("%d", &n);
    /*6种状态
    0--用了2,剩0,1,3
   1--用了0,2,剩1,3
   2--用了2,3,剩0,1
   3--用了0,1,2,剩3
   4--用了0,2,3,剩1
   5--0,1,2,3
    */
    for (int i = 1; i <= n; i++) {
        int j = i - 1;
        f[i][0] = 1;
        f[i][1] = (f[j][0] + f[j][1] * 2) % mod;
        f[i][2] = (f[j][0] + f[j][2]) % mod;
        f[i][3] = (f[j][1] + f[j][3] * 2) % mod;
        f[i][4] = (f[j][1] + f[j][2] + f[j][4] * 2) % mod;
        f[i][5] = (f[j][3] + f[j][4] + f[j][5] * 2) % mod;
    }
    printf("%lld\n", f[n][5]);
    return 0;
}
#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int mod=1e9+7;
int n;
ll dp0=1,dp1,dp2,dp4,dp3,dp5;

int main() {
    scanf("%d", &n);
    /*
    0--用了2,剩0,1,3
   1--用了0,2,剩1,3
   2--用了2,3,剩0,1
   3--用了0,1,2,剩3
   4--用了0,2,3,剩1
   5--0,1,2,3
    */
    for (int i = 1; i <n; i++) {
        ll pre0 = dp0, pre1 = dp1, pre2 = dp2, pre3 = dp3, pre4 = dp4, pre5 = dp5;
        dp0 = pre0;
        dp1 = (pre0 + pre1 * 2) % mod;
        dp2 = (pre0 + pre2) % mod;
        dp3 = (pre1 + pre3 * 2) % mod;
        dp4 = (pre1 + pre2 + pre4 * 2) % mod;
        dp5 = (pre3 + pre4 + pre5 * 2) % mod;
    }
    printf("%lld\n", dp5);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值