uva 12333

原题


本来以为只是大整数计算的问题, 用了很笨的方法, 计算出前100000个斐波那契数然后逐一匹配

结果等了半天也没算出来... 后来一查发现到后面会出现10^2W次方的位数, 显然不能用这种方法

后来google了答案, 才想起来这是前缀查找的, 应该用字典树来存储每个数, 而且只需要存储前40位即可.....原先想的太复杂了啊

再把别人AC的思路描述一下把:


整个字典树用数组存储, 每用到(增加)一个结点就在数组中存一个, 这样可以最大限度地节省空间

每个结点内包含10个子节点的下标, 以及一个Res, 存储经过这个结点且序号最小的一条路径(即一个斐波那契数) 

首先从头开始逐个计算斐波那契数, 就用F(n) = F(n-1) + F(n-2)的式子, 每个F(N)用高精度类型存储

不过为了节省空间, 可以循环利用, 算完F(N)之后, F(N-2)就不需要再用了, 而且原来的F(N)就变成了F(N-1), F(N-1)变成F(N-2),

因此可以把F(N-2)的内存用来存储F(N), 以此类推

每算出一个F(N), 把它的数值和序号都插入字典树中, 注意存入的时候, 如果要创建新的结点, 就把这个结点的Res置为当前的N(序号)

如果插入的时候深度超过40了就停止插入, 因为输入最多也就到40了


基本上是这样, 下面是仿照别人的代码写的

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string>
#include <vector>
#include <set>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <list>
#include <cassert>
#include <iomanip>

using namespace std;
const int MAXN 	= 100000;
const int MAXBIT= 40;
const int BASE 	= 100000000;
typedef long long LL;

/*
uva 12504
关键 :  1. 存储->高精度整数( 大于40位的计算 )
		2. 查找->字典树( 前缀匹配 ) 
		3. 字典树结点的数据结构 : 只用一个int型数组存储子节点的下标, 外加一个bool标记是否终点即可
		   注意要将整棵树只存在一个结点数组中, 因为结点过于稀疏, 
		   不可能定义数组长度为10^(树高), 只需【用到一个就增加一个】即可 
*/

struct Trie{
	int link[10];						// 存储子节点的下标 
	int Res;							// 标记这个数(如果是的话)是第几个斐波那契数 
} Tries[40000000];						// Tries[0]为默认结点(根节点) 

char buf[100];							// 用来存储把大整数转换成字符串后的字符串 
										// 定为全局变量是为了方便char数组(字符串)的传递
										// (若返回在转换函数内定义的char数组则不可行, 函数内定义的数组在栈中会被回收) 

char * IntToStr(int arr[], int len){	// 返回char指针可行, 因为指向的内存在堆中而非此函数的栈中 
	char * p = buf;
	for(int i=len-1, cnt=0; i>=0 && cnt<6 ; cnt++, i--){

		*(p++) = (arr[i]/10000000)%10 + '0';		// 最高位 
		*(p++) = (arr[i]/1000000)%10 + '0';
		*(p++) = (arr[i]/100000)%10 + '0';
		*(p++) = (arr[i]/10000)%10 + '0';
		*(p++) = (arr[i]/1000)%10 + '0';
		*(p++) = (arr[i]/100)%10 + '0';
		*(p++) = (arr[i]/10)%10 + '0';
		*(p++) = (arr[i]/1)%10 + '0';
	}
	p = buf;
	while( *p=='0' ) p++;
	return p;
}

void TrieIns(char const * num,int val){
	
	static int TrieNum = 0;
	int idx = 0, m = 0, len = strlen(num);
//	printf("%5d:%s\n",val,num);
	for(int i=0; i<len && i<MAXBIT; i++){
		if( Tries[idx].link[num[i]-'0']==0 ){				// 没有创建过的结点 
			Tries[idx].link[num[i]-'0'] = ++TrieNum;
			Tries[TrieNum].Res = val;						
			// 为什么在这里要填入Res : 因为这道题只要模糊地找出有某个前缀的斐波那契数,
			// 并不是要准确地找出斐波那契数的值, 并且模糊找的那个应该是数列中序号最小的
			// 因此只需要在第一次到达的结点处填入当前的序号即可 
		}
		idx = Tries[idx].link[num[i]-'0'];
	}
	
}

void Generate(){
	int fib[3][100000], len;				// 用于计算斐波那契数列(求幂)的F(N-2), F(N-1), F(N) 
	int a = 0, b = 1, c = 2;
	memset(Tries,0,sizeof(Tries));
	len = 1;
	memset(fib,0,sizeof(fib)); 
	fib[a][0] = fib[b][0] = 1;
	TrieIns(IntToStr(fib[a],len),0);						// 斐波那契数列下标为0和1的都等于1, 只插入其中一个即可 
	for(int i=2; i<100000; i++ ){
		for(int j=0; j<len; j++){
			fib[c][j] = fib[a][j] + fib[b][j]; 
		}
		for(int j=0; j<len; j++){
			if( fib[c][j]>=BASE ){
				fib[c][j+1] += fib[c][j]/BASE;
				fib[c][j] %= BASE;
			}
		}
		if( fib[c][len]>0 ) len++;
		TrieIns(IntToStr(fib[c],len),i);
		a++, b++, c++;
		a %= 3;
		b %= 3;
		c %= 3;
	}
	return ;
}

int main(){
//	freopen("input2.txt","r",stdin);
//	freopen("output.txt","w",stdout);
	Generate();
	char input[MAXBIT+10];
	int T,Case = 0;
	scanf("%d ",&T);
	while( T-- ){
		scanf("%s",input);
		int i, idx = 0;
		for(i=0; i<strlen(input); i++){
			if( Tries[idx].link[input[i]-'0']==0 ){			// 没有到达过的结点 
				break;
			}
			idx = Tries[idx].link[input[i]-'0'];
		}
		printf("Case #%d: ",++Case);
		if( i<strlen(input) ){
			printf("-1");
		}else{
			printf("%d",Tries[idx].Res);
		}
		printf("\n");
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值