百度之星2012年冬季邀请赛第一题分析

问题描述

[描述]

    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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值