问题描述
[描述]
du熊对数学一直都非常感兴趣。最近在学习斐波那契数列的它,向你展示了一个数字串,它称之为“斐波那契”串:
11235813471123581347112358........
聪明的你当然一眼就看出了这个串是这么构造的:
1.先写下两位在0~9范围内的数字a, b,构成串ab;
2.取串最后的两位数字相加,将和写在串的最后面。
上面du熊向你展示的串就是取a = b = 1构造出来的串。
显然,步骤1之后不停地进行步骤2,数字串可以无限扩展。现在,du熊希望知道串的第n位是什么数字。
[输入]
输入数据的第一行为一个整数T(1 <= T <= 1000), 表示有T组测试数据;
每组测试数据为三个正整数a, b, n(0 <= a, b < 10, 0 < n <= 10^9)。
[限制]
Time Limit: 2000/1000ms (C/Other)
Memory Limit: 65535/32768K (C/Other)
[来源]
http://astar.baidu.com/index.php?r=home/detail&id=2
(1)每次需要构造的时候,只依赖于末尾的两个数有关,而和前面的其他数字无关。
(2)因为只依赖于末尾的两个数字,所有最多可能的状态数为10*10=100,因此如果不断循环下去必然存在循环节
(3)编码细节需要多加以注意
实现
#include <stdio.h>
#include <vector>
#include <string>
#include <assert.h>
using std::vector;
using std::string;
struct next_t
{
unsigned int nextu;
unsigned int nextv;
unsigned int carry;
public:
next_t(unsigned int u = 0,unsigned int v = 0,unsigned int c = 0):nextu(u),nextv(v),carry(c) { ; }
};
struct chain_t
{
string prefix;
string cycle;
};
int main()
{
static const unsigned int digit_set_size = 10;
next_t nexts[digit_set_size][digit_set_size]; // 状态迁移表
for(unsigned int i = 0;i < digit_set_size;++i)
{
for(unsigned int k = 0;k < digit_set_size;++k)
{
unsigned int x = i + k,u = 0,v = 0,c = 0;
if(x < digit_set_size) u = k,v = x,c = 0;
else u = x/digit_set_size,v = x%digit_set_size,c = 1;
nexts[i][k] = next_t(u,v,c);
}
}
chain_t chains[digit_set_size][digit_set_size];
for(unsigned int i = 0;i < digit_set_size;++i)
{
for(unsigned int k = 0;k < digit_set_size;++k) // 计算每种状态的状态链,直到循环节出现
{
size_t serial[digit_set_size][digit_set_size] = { 0 }; // 每种状态在trace中的位置
memset(serial,0xff,sizeof(serial));
string trace;trace.push_back('0'+i); //
trace.push_back('0'+k);
unsigned int p = i,q = k,z = 0;
for(;serial[p][q] == (size_t)(-1);++z) // 注意一种情况:257 1235813471 12
{
assert(trace.size() >= 2);
serial[p][q] = trace.size() - 2;
next_t r = nexts[p][q];
p = r.nextu;q = r.nextv;
if(0 != r.carry) trace.push_back('0'+p);
trace.push_back('0'+q);
}
assert(serial[p][q] + 2 < trace.size());
size_t prefix_len = serial[p][q];
chains[i][k].prefix = trace.substr(0,prefix_len);
chains[i][k].cycle = trace.substr(prefix_len,trace.size()-prefix_len-2); // 注意:最末尾的2位其实是已经重复的状态,这两位有可能都是最后添加的,也有可能有一位是前一次添加的
//printf("(%u,%u) %s,%s\n",i,k,chains[i][k].prefix.c_str(),chains[i][k].cycle.c_str());
}
}
unsigned int nCases = 0;scanf("%d",&nCases);
for(unsigned int iCases = 1;iCases <= nCases;++iCases)
{
unsigned int a = 0,b = 0,n = 0;scanf("%d%d%d",&a,&b,&n);
const chain_t& r = chains[a][b];
unsigned int loopdis = (unsigned int)(r.prefix.size());
unsigned int loopsize = (unsigned int)(r.cycle.size());
unsigned int ans = 0;
if(n <= loopdis) ans = (unsigned int)(r.prefix[n-1] - '0'); // 注意等号
else
{
unsigned int pos = (n - loopdis)%loopsize;
if(0 == pos) pos = loopsize; // 注意整除的情况
ans = (unsigned int)(r.cycle[pos-1] - '0');
}
printf("Case #%u: %u\n",iCases,ans);
}
return 0;
}