我们把一个数称为有趣的,当且仅当:
这个算法题显然要用到动态规划的方法解决,关键是找出递推式
思路可以由以下方向入手理解:
将n个数字组成的有趣数的个数记为g(n)
需要明确:任何一个n位有趣数,都可以看做是n-1位数,再在其后面追加0-3其中一个数而组成的
在这个组合过程中,有以下两种情况:
对于情况1,前n-1位数是由0,1,2,3组成的有趣数,末尾追加1或3(因为要满足有趣数的定义,最后一位不可能是0或2)
对于情况2,又分为几种情况:
因此,可以得出结论,g(n)和g(n-1)的关系不只与n有关,还与前n-1位数的状态有关,用一维参数变量就很难表示其递推关系了,所以需用二维参数,我们再记f(n,s)为n位状态s下的"类有趣数"的个数。而当状态s为0-3都至少出现一次时,也就是四个数都出现过的"类有趣数",就是我们所求的有趣数。
明确了以上的推导思路过程,再来看下面推导递推式的过程:
上面得出,我们要用f(n,s)为n位状态s下的"类有趣数"的个数
现列出所有类有趣数的形式(状态):
状态0:由2组成
状态1:由2,0组成
状态2:由2,3组成
状态3:由0,1,2组成
状态4:由0,2,3组成
状态5:由0,1,2,3组成(此时为有趣数)
即我们所要求的是f(n,5)的值
显然
(状态5时在末尾可以追加1、3,状态3时可以追加3,状态4时可以追加1,所有分别乘2、1、1)
类推分别得出f(n,4)f(n,3)f(n,2)f(n,1):
有了递推式,程序实现就简单多了,这里写了一个自底向上的动态规划写法供参考。
#include
#define PRIME 1000000007
using namespace std;
_int64 f[1000][6]; //_int64支持vc6.0
void main(){
int n;
cin >> n;
f[1][0] = 1;
for(int i = 2;i <= n;i++){
f[i][0] = 1;
f[i][1] = (2 * f[i-1][1] + f[i-1][0]) % PRIME;
f[i][2] = (f[i-1][2] + f[i-1][0]) % PRIME;
f[i][3] = (2 * f[i-1][3] + f[i-1][1]) % PRIME;
f[i][4] = (2 * f[i-1][4] + f[i-1][1] + f[i-1][2]) % PRIME;
f[i][5] = (2 * f[i-1][5] + f[i-1][3] + f[i-1][4]) % PRIME;
}
printf("%I64d\n",f[n][5]);
}