UVA - 12333 Revenge of Fibonacci
/* 参考: http://www.cnblogs.com/kunsoft/p/5312726.html 收获: 1. 了解了字典树,可参考几个博客: http://blog.csdn.net/u012469987/article/details/43874051 http://blog.csdn.net/u011787119/article/details/46991691 2.复习了常量指针和指向常量的指针 http://blog.csdn.net/luoweifu/article/details/45600415 3.复习了之前在看《挑战》时,只是略有了解的滚动数组 http://blog.csdn.net/u012577123/article/details/39211361 滚动数组最大的意义,在于缩减空间方面,对时间的影响不大 说明: 4. 关于 if(e - s > 50) ++s; 这句代码,一开始我一直不理解原因,也不明白为什么要取50,后来查阅了有关题解,才明白过来 因为要求精确到前40位,为了避免进位的误差,所以我们计算的时候,肯定要比40算的多一些(否则,如果真的取40,可能会有一些进位误差没有考虑到,例如有某个F数组的元素,算出来有进位,但是没有进行进位处理,如果这个错误的位置,恰好在我们需要的前40的精度里,那么就会WA了) 最后试验的结果时, e - s至少为49,当然,取200什么的也没问题,这个主要是代表了一个精度,让我们知道,我们求得的斐波拉契数列,大概有前多少位的范围,是精确可靠,可以相信的,所以往多了取当然没问题,但是不能取少了,所以最合适的是50、60的样子 另附上其他博客里的一些解释 4.1. 因为只是需要前40位,而且fibonacci数列上升速度很快, 所以我们保留60位的精度就足够了。 http://www.cnblogs.com/Jadon97/p/6922619.html 4.2. 因为只要前40位数字。所以在加的时候长度大于50左右就截掉个位上的,保留高位。 (这是我觉得最清楚而言简意赅的解释了,因为斐波拉契数可能会有很多位,所以我们要截断,可是只截断40位,可能有未处理的进位导致WA,所以我们截断前50位左右) http://www.cnblogs.com/kuangbin/archive/2012/09/06/2673897.html 4.3.我们可以把斐波那契数列精确到50多位,然后只存40位即可,这样就防止进位的误差。在斐波那契数列加法过程中,我们只把它的前50多位进行相加,不然存不下。 http://blog.csdn.net/u012965373/article/details/40636371 */
#include <bits/stdc++.h> using namespace std; struct Node { int id; Node* next[10]; Node() { id = -1; memset(next, 0, sizeof(next)); } }; char Fib[50], In[50]; int F[2][1024000]; Node *const root = new Node(); void add_node (char* str, int id) { Node *u = root; for (int i = 0, len = (int)strlen(str); i < len && i <= 40; i++) { int v = str[i] - '0'; if (!u->next[v]) u->next[v] = new Node(); u = u->next[v]; if (u->id == -1) u->id = id; //同一个长度n的斐波拉契数,截断为前1~i(1 <= i <= n)位以后,所有的截断方式,都具有相同的序号,表示以该字符串开头的斐波拉契数,对应的序号都是id } } int query(char* str) { Node *u = root; // 在树上顺序搜索str的每一个字符 for (size_t i = 0, len = strlen(str); i < len; i++) { u = u->next[str[i] - '0']; // 若为空集,表示不存以此为前缀的斐波拉契序号 if (!u) return -1; } // 返回以该信息为前缀的斐波拉契序号 return u->id; } void init() { memset(F, 0, sizeof(F)); F[0][0] = F[1][0] = 1; int s = 0, e = 1; add_node((char *)"1", 0); add_node((char *)"1", 1); for(int i = 2; i < 100000; ++i) { int p = i%2, q = (i+1)%2; for(int j = s; j < e; ++j) F[p][j] = F[p][j] + F[q][j]; for(int j = s; j < e; ++j) if(F[p][j]>=10) { F[p][j] %= 10; F[p][j+1] += 1; } if(F[p][e]) ++e; if(e - s > 50) ++s; int r = e - 1, cnt = 0; memset(Fib, 0, sizeof(Fib)); while(r >= 0 && cnt<40) //<40的限定,是因为输入不超过40个数字,因此初始化时,我们也只是取前40个数,若位数太多,有可能超过int的范围,导致运行错误(如果不对cnt做限定,则将RE) Fib[cnt++] = F[p][r--] + '0'; add_node(Fib, i); } } int main() { cin.tie(0); cin.sync_with_stdio(false); init(); int t; cin >> t; for (int i = 1; i <= t; i++) { cin >> In; cout << "Case #" << i << ": " << query(In) << endl; } return 0; }