数位dp。(非常规)
这种题光看代码真是头疼,想了一个小时才想明白当时的思路。
思路如下:
题目问你x
位有趣的数有多少个,记为c[x]
,那么就要想出来c[x]
怎么用c[x-1]
表示。
x
位有趣的数一定都可以由一个x-1
位的数m
在末尾添加一个数字而构造出来,分为两种情况:
m
是有趣的数
可以想到,只能在末尾添加1
或3
,这两种添加方式都一定有效。m
不是有趣的数
若在m
后添加一个数字能使其变为有趣的数,则m
一定是少一个数字(m
只违背规则1),且只能少1
或者少3
。少什么补什么就好了,一定有效。
这样定义了新的两种数A,B
:
A
:不是有趣的数,在其末尾添加1
就能变为有趣的数。B
:不是有趣的数,在其末尾添加3
就能变为有趣的数。
对于指定位数的A,B
的个数怎么计算?重复使用上述方法,逐渐消减定义复杂度。
#include <iostream>
using namespace std;
const int MOD = 1e9 + 7;
long long dp[1001][6];
void cal(int n)
{
for (int i = 5; i <= n; i++)
{
dp[i][0] = dp[i - 1][0]; // 只有2
dp[i][1] = dp[i - 1][1] + dp[i - 1][0]; // 2,3
dp[i][2] = 2 * dp[i - 1][2] + dp[i - 1][0]; // 2,0
dp[i][3] = 2 * dp[i - 1][3] + dp[i - 1][1] + dp[i - 1][2]; // 2,0,3
dp[i][4] = 2 * dp[i - 1][4] + dp[i - 1][2]; // 2,0,1
dp[i][5] = 2 * dp[i - 1][5] + dp[i - 1][3] + dp[i - 1][4]; // 全部
dp[i][2] %= MOD;
dp[i][3] %= MOD;
dp[i][4] %= MOD;
dp[i][5] %= MOD;
}
}
void init()
{
dp[4][0] = 1;
dp[4][1] = 3;
dp[4][2] = 7; // 失之毫厘 谬以千里
dp[4][3] = 9;
dp[4][4] = 5;
dp[4][5] = 3;
}
int main()
{
int N;
cin >> N;
init();
cal(N);
cout << dp[N][5] << endl;
return 0;
}