竞赛小白,最近在刷紫书,终于刷到了这么一个具有思维挑战性的题目,从昨晚到现在,不断写不断debug不断参考资料,终于花了五六个小时A了这个题。
这个题对于竞赛小白的我学到了很多东西。
1.首先,大数高精度加减:
一般来说我都习惯使用链表进行高精度加减,但是,这个题之后,使我摒弃了高精度使用链表保存数字的想法,因为,链表由于其数据结构的特点,非常占用空间,这个题100000个数,全用链表保存会直接导致内存爆炸,甚至把我的电脑跑蓝屏了。其次,链表增添数字需要设置头指针,还需要设置前驱指针,代码复杂,容易遗漏,于是以后,一旦遇到高精度,将使用string类来保存数字
对于特别大数且题目对于精确度要求低(此题只要求前40位)的高精度加减,可以采取只对前面几位进行处理,因为像此题斐波那契数列到100000个数字规模已经很大,这时候完全处理肯定会超时。当然只对前面几位处理应当有如下要求,才能保证前面的位无误差:
(1)记录数字的位数(我使用的是结构体),如果两个加数位数不等,较小的数丢掉后面的几个数字,丢掉数字的个数位两数位数之差。加完后每进一位,大数的数字位数+1。
(2)因为每一次加完数字都取固定的位数,为了进一步缩小误差,将加完的数取固定位数时丢掉的那个数字记录下来,如果存在(1)中的现象,每次最开始要让较小数的最后一位加上这个数字,取进位值,再第一轮加法中加上这个进位值。
2.字典树知识的巩固
暑假自学数据结构刷leetcode的时候了解过字典树,也做过一些字典树的基础题。但这题一开始没想用字典树,但是如果每次都要遍历100000个Fibonacci前缀,时间复杂度肯定很高。于是经搜查资料知道,得用字典树。(字典树不仅能存储字符串的前缀,也能存数字的前缀,其实这个数字也是字符串,以后看到求前缀,可以考虑使用字典树)。于是丢了两个月的字典树又复习了一遍。以往在leetcode上写题,字典树结构体不用自己初始化,但是自己写程序,就必须在创立结点的时候自己memset以下,否则就会出现无法初始化下标访问错误的情况。
3.代码调试
因为这题很容易超时,所以我不得不在调试时记录程序运行的时间。于是此处学到了记录程序运行时间的方法。首先,调用windows.h库,然后
DWORD start,end;
start=GetTickCount();
<code_block>
end=GetTickCount()-start;
cout<<"time:"<<end<<endl;
codeblock是你想要测试运行时间的代码块。使用GetTickCount记录程序运行的起止时间。相减就可以得到运行时间。(单位:毫秒)
附本题代码:
#include<iostream>
#include<string>
#include<unordered_map>
#include<vector>
#include<string.h>
using namespace std;
struct Num_Trie
{
Num_Trie *next_node[10];
int id=-1;
Num_Trie(int _id):id(_id){
memset(next_node,NULL,sizeof(next_node));
}
};
struct NumStr
{
string f;
int size;
int throw_num=0;
NumStr(string _f,int _size):f(_f),size(_size){}
};
void add(NumStr& f1,NumStr& f2){
NumStr s_copy=f1;
int add=0;
int i=f1.f.size()-1;
int j;
if(f1.size>f2.size&&f1.size>60){
add=(f1.throw_num+f2.f.back()-'0')/10;
f2.f=f2.f.substr(0,f2.f.size()-f1.size+f2.size);
}
j=f2.f.size()-1;
while(i>=0&&j>=0){
int sum=(f1.f[i]-'0'+f2.f[j]-'0'+add);
f1.f[i]=(sum%10)+'0';
add=sum/10;
i--;
j--;
}
while(i>=0){
int sum=(f1.f[i]-'0'+add);
f1.f[i]=(sum%10)+'0';
add=sum/10;
i--;
}
if(add>0){
//cout<<add<<endl;
f1.f=char(add+'0')+f1.f;
f1.size++;
}
if(f1.f.size()>60){
//cout<<f1.size()<<endl;
f1.throw_num=f1.f.back()-'0';
f1.f=f1.f.substr(0,60);
}
//cout<<f1.size<<endl;
f2=s_copy;
return;
}
Num_Trie* my_tree;
void init(){
NumStr f1("1",1);
NumStr f2("1",1);
Num_Trie* my_tree_root=new Num_Trie(-1);
my_tree=my_tree_root;
Num_Trie* new_node_first=new Num_Trie(0);
my_tree->next_node[1]=new_node_first;
for(int i=2;i<100000;i++){
add(f1,f2);
string num=f1.f.substr(0,40);
//cout<<"i:"<<i<<" "<<num<<endl;
Num_Trie* point=my_tree;
for(int j=0;j<num.size();j++){
if(point->next_node[num[j]-'0']==nullptr){
Num_Trie* new_node=new Num_Trie(i);
point->next_node[num[j]-'0']=new_node;
point=point->next_node[num[j]-'0'];
}
else{
if(point->next_node[num[j]-'0']->id==-1||point->next_node[num[j]-'0']->id>i){
point->next_node[num[j]-'0']->id=i;
}
point=point->next_node[num[j]-'0'];
}
}
}
return;
}
int Get_id(string num){
Num_Trie* point=my_tree;
for(int i=0;i<num.size();i++){
if(point->next_node[num[i]-'0']==nullptr||point->next_node[num[i]-'0']->id==-1){
return -1;
}
else{
point=point->next_node[num[i]-'0'];
}
}
return point->id;
}
int main(){
init();
int all_num;
cin>>all_num;
int epoch=0;
while(all_num){
epoch++;
string num;
cin>>num;
cout<<"Case #"<<epoch<<": "<<Get_id(num)<<endl;
all_num--;
}
return 0;
}
以上为个人记录,供以后复习所用,如有错误请指出!