问题分析
Problem Analyse 递推题
Algorithm Analyse 比起以前做过的递推题,这一题算比较麻烦的了(当然,原因是我没有想到
好的方法,如果你有更方便的方法,欢迎提供大家一起学习)。
如果没有任何条件限制,A、B、C、D组成长度为n的字符串,其个数应该为:4n。
因为有了A、C需要出现偶数次的要求,就出现合法和不合法的不同分组。
在不合法的组里,又有
1.A出现奇数次、C出现偶数次;
2.C出现奇数次、A出现偶数次;
3.A出现奇数次、C出现奇数次;
三种情况。
我们用数组
f[n][0]保存长度为n,合法的字符串的个数。
f[n][1]保存长度为n,仅A出现奇数次的字符串的个数。
f[n][2]保存长度为n,仅C出现奇数次的字符串的个数。
f[n][3]保存长度为n,A、C出现奇数次的字符串的个数。
f[n][0]
长度为n-1的合法字符串在末尾加上一个B或者D,都可以变成长度为n的合法字符串。
长度为n-1的仅A出现奇数次的字符串再在末尾加上一个A,也可以变成合法字符串。
长度为n-1的仅C出现奇数次的字符串再在末尾加上一个C,也可以变成合法字符串。
所以,f[n][0] = 2 × f[n-1][0] + f[n-1][1] + f[n-1][2];
f[n][1]
长度为n-1的合法字符串在末尾加上A,都可以变成长度为n的仅A出现奇数次的字符串。
长度为n-1的仅A出现奇数次的字符串再在末尾加上一个B或者D,也可以变成仅A出现奇数次的字
符串。
长度为n-1的A、C出现奇数次的字符串再在末尾加上一个C,也可以变成仅A出现奇数次的字符串
。
所以,f[n][1] = 2 × f[n-1][1] + f[n-1][0] + f[n-1][3];
f[n][2]
长度为n-1的合法字符串在末尾加上C,都可以变成长度为n的仅C出现奇数次的字符串。
长度为n-1的仅C出现奇数次的字符串再在末尾加上一个B或者D,也可以变成仅C出现奇数次的字
符串。
长度为n-1的A、C出现奇数次的字符串再在末尾加上一个A,也可以变成仅C出现奇数次的字符串
。
所以,f[n][2] = 2 × f[n-1][2] + f[n-1][0] + f[n-1][3];
f[n][3]
长度为n-1的A、C出现奇数次的字符串在末尾加上一B或者D,都可以变成长度为n的A、C出现奇数
次的字符串。
长度为n-1的仅A出现奇数次的字符串再在末尾加上一个C,也可以变成A、C出现奇数次的字符串
。
长度为n-1的仅C出现奇数次的字符串再在末尾加上一个A,也可以变成A、C出现奇数次的字符串
。
所以,f[n][3] = 2 × f[n-1][3] + f[n-1][1] + f[n-1][2];
综上所述,我们得到:
f[n][0] = 2 × f[n-1][0] + f[n-1][1] + f[n-1][2]; ①
f[n][1] = 2 × f[n-1][1] + f[n-1][0] + f[n-1][3]; ②
f[n][2] = 2 × f[n-1][2] + f[n-1][0] + f[n-1][3]; ③
f[n][3] = 2 × f[n-1][3] + f[n-1][1] + f[n-1][2]; ④
f[1][0] = 2
f[1][1] = 1
f[1][2] = 1
f[1][3] = 0
/**** 搞出这个我就很哈皮的去敲快速幂了。。。然而大牛。。。 ****/
发现f[1][1]与f[1][2]初始状态相同,而且以后迭代方程也相同,所以f[n][1] = f[n][2]
又有f[n][0] + f[n][3] = f[n][1] + f[n][2]
∵f[n][0] + f[n][1] + f[n][2] + f[n][3] = 4^n
∴f[n][0] + f[n][3] = f[n][1] + f[n][2] = 2 × 4^(n-1)
∴f[n-1][1] + f[n-1][2] = 2 × 4^(n-2)
∴f[n][0] = 2 × f[n-1][0] + f[n-1][1] + f[n-1][2] = 2 × f[n-1][0] + 2 × 4^(n-2)
我们得到:
f[n][0] = 2 × f[n-1][0] + 2^(2n-3)
f[n-1][0] = 2 × f[n-2][0] + 2^(2n-5)
┋
f[n-m][0] = 2 × f[n-m-1][0] + 2^(2n-2m-3)
┋
f[2][0] = 2 × f[1][0] + 2^1
f[1][0] = 2
开始一层层往下迭代:
f[n][0]
= 2 × f[n-1][0] + 2^(2n-3)
= 2^2 × f[n-2][0] + 2^(2n-4) + 2^(2n-3)
┋
= 2^m × f[n-m][0] + 2^(2(n-m)-1+m-1) + … + 2^(2n-3)
= 2^(n-1) × f[1][0] + 2^(n-1) + 2^n +… + 2^(2n-3)
f[1][0] = 2;
∴f[n][0] = 2^n + 2^(n-1) + 2^n +… + 2^(2n-3) = 2^(2n-2) + 2^(n-1)
算法实现
Programing 公式得到了:f(n) = 2^(2n-2) + 2^(n-1)
但就这样直接编程那是不可能实现的,因为n的范围1≤N<=2^64
怎么的范围,是不能求出f(n)的。所以还得找其他规律。
因为题目只要求输出最后2位数,我们依次输出2的n的最后两位看看…
2^0 1
2^1 2
2^2 4
2^3 8
2^4 16
2^5 32
2^6 64
2^7 28
2^8 56
2^9 12
2^10 24
2^11 48
2^12 96
2^13 92
2^14 84
2^15 68
2^16 36
2^17 72
2^18 44
2^19 88
2^20 76
2^21 52
2^22 4
到了2^22时,末尾2位又变成4,与2^2一样,这时候就进入了一个循环了(每20个一次循环)。
所以,结果只能是这22个中的一个。只有n=0 和 n=1是需要特殊考虑的。其他n就等于2(n-2) %20 + 2的值了。
#include <iostream>
using namespace std;
int p2[30] = {1};
int po(__int64 x)
{
if (x>1) x = (x - 2)% 20 + 2;
if (x<0) return 1;
return p2[x];
}
int main()
{
for (int i=1;i<24;++i) p2[i] = (p2[i-1]<<1) % 100;
int t;
while (scanf("%d", &t), t)
{
for (int i=1;i<=t;++i)
{
__int64 n, a;
scanf("%I64d", &n);
a = po(2*n-2) + po(n-1);
printf("Case %d: %d\n", i, a%100);
}
printf("\n");
}
return 0;
}