题目链接:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3755
题目大意:
意思不难理解,就是找到以某个字符串为前缀的最小的斐波拉契数列,并输出是第几个斐波拉契数。找不到就输出-1;
思路:大数加法,字典树。
ps:通过这道题接触到了大数加法。(虽然之前在刘汝佳的那本算法书里看到过阶乘的,但当时匆匆略过了)
该题是多组输入,我们就对斐波拉契数列预处理,将1——100000的斐波拉契数列找出来。
将它们存入字典树。长度超过40的就只压入长度为41的前缀。
然后在每个节点存入以当前前缀为最小的斐波拉契数的序号。
ps:斐波拉契数列不能多存了一个!只要你超过了100000个就wa,它卡了这个点;
之所以不全部存入是为了节省空间。不然会RE。
然后代码应该就能AC了。
ps:本人代码跑了8330ms,感觉都要tle了。看到他们都是几百ms就ac了,不造怎么写的。时间问题就没有去看。
ps:知道在哪时间耗了那么久了,就是大数加法的那里最好判断一下是否数据超过了49位,是的话我们就舍去后面的位数。因为它前缀查询是不超过40位,那么我们就只需考虑前40位,但是会有误差,所以我们为了保险起见,我们选择49位。这样就避免误差了。现在附上第二份代码,耗时770ms,1s也能AC了
ps:不知道为什么我今天用自己的代码提交出现TLE,但是过段时间重复提交几次后又AC了,可能因为是服务器的问题吧。。。
代码如下
1 #include<iostream> 2 #include<stdio.h> 3 using namespace std; 4 string fib(string s1,string s2) 5 { 6 if(s1.length()<s2.length()) 7 { 8 string temp=s1; 9 s1=s2; 10 s2=temp; 11 } 12 int i,j; 13 for(i=s1.length()-1,j=s2.length()-1;i>=0;i--,j--) 14 { 15 s1[i]=char(s1[i]+(j>=0?s2[j]-'0':0)); 16 if(s1[i]-'0'>=10) 17 { 18 s1[i]=char((s1[i]-'0')%10+'0'); 19 if(i) s1[i-1]++; 20 else s1='1'+s1; 21 } 22 } 23 return s1; 24 } 25 string a[100002]; 26 int cnt=1; 27 int sum[10000050][11]; 28 int num[10000050]; 29 void add(string s,int number) 30 { 31 int root=0; 32 for(int i=0;i<s.length()&&i<42;i++) 33 { 34 int xx=s[i]-'0'; 35 if(!sum[root][xx]) 36 { 37 sum[root][xx]=cnt++; 38 num[sum[root][xx]]=number;//存入最小的前缀,因为数列本身就是从小到大排,存入序号就ok 39 } 40 root=sum[root][xx]; 41 } 42 } 43 int Find(string s) 44 { 45 int root=0; 46 for(int i=0;i<s.length();i++) 47 { 48 int xx=s[i]-'0'; 49 root=sum[root][xx]; 50 if(root==0) 51 return 0; 52 } 53 return num[root]; 54 } 55 int main() 56 { 57 a[1]="1"; 58 a[2]="1"; 59 add(a[1],1); 60 add(a[2],2); 61 for(int i=3;i<=100000;i++)//预处理 62 { 63 a[i]=fib(a[i-1],a[i-2]); 64 add(a[i],i); 65 } 66 int T; 67 while(cin>>T) 68 { 69 int t=0; 70 string st; 71 while(T--) 72 { 73 t++; 74 cin>>st; 75 int tmp=Find(st); 76 if(tmp){ 77 cout<<"Case #"<<t<<": "<<tmp-1<<endl;//因为题目从0开始算,所以减一 78 }else{ 79 cout<<"Case #"<<t<<": "<<-1<<endl; 80 } 81 } 82 } 83 return 0; 84 }
优化后的代码:
1 #include<iostream> 2 #include<stdio.h> 3 using namespace std; 4 string fib(string s1,string s2) 5 { 6 if(s1.length()<s2.length()) 7 { 8 string temp=s1; 9 s1=s2; 10 s2=temp; 11 } 12 int i,j; 13 if(s2.length()>49) 14 { 15 for(i=49,j=s2.length()+49-s1.length();i>=0;i--,j--) 16 { 17 s1[i]=char(s1[i]+(j>=0?s2[j]-'0':0)); 18 if(s1[i]-'0'>=10) 19 { 20 s1[i]=char((s1[i]-'0')%10+'0'); 21 if(i) s1[i-1]++; 22 else s1='1'+s1; 23 } 24 } 25 } 26 else 27 { 28 for(i=s1.length()-1,j=s2.length()-1;i>=0;i--,j--) 29 { 30 s1[i]=char(s1[i]+(j>=0?s2[j]-'0':0)); 31 if(s1[i]-'0'>=10) 32 { 33 s1[i]=char((s1[i]-'0')%10+'0'); 34 if(i) s1[i-1]++; 35 else s1='1'+s1; 36 } 37 } 38 } 39 return s1; 40 } 41 string a[100002]; 42 int cnt=1; 43 int sum[10000050][11]; 44 int num[10000050]; 45 void add(string s,int number) 46 { 47 int root=0; 48 for(int i=0;i<s.length()&&i<42;i++) 49 { 50 int xx=s[i]-'0'; 51 if(!sum[root][xx]) 52 { 53 sum[root][xx]=cnt++; 54 num[sum[root][xx]]=number; 55 } 56 root=sum[root][xx]; 57 } 58 } 59 int Find(string s) 60 { 61 int root=0; 62 for(int i=0;i<s.length();i++) 63 { 64 int xx=s[i]-'0'; 65 root=sum[root][xx]; 66 if(root==0) 67 return 0; 68 } 69 return num[root]; 70 } 71 int main() 72 { 73 a[1]="1"; 74 a[2]="1"; 75 add(a[1],1); 76 add(a[2],2); 77 for(int i=3;i<=100000;i++) 78 { 79 a[i]=fib(a[i-1],a[i-2]); 80 add(a[i],i); 81 } 82 int T; 83 while(cin>>T) 84 { 85 int t=0; 86 string st; 87 while(T--) 88 { 89 t++; 90 cin>>st; 91 int tmp=Find(st); 92 if(tmp){ 93 cout<<"Case #"<<t<<": "<<tmp-1<<endl; 94 }else{ 95 cout<<"Case #"<<t<<": "<<-1<<endl; 96 } 97 } 98 } 99 return 0; 100 }