UVA 12333 斐波那契的复仇 今日竞赛刷题总结

竞赛小白,最近在刷紫书,终于刷到了这么一个具有思维挑战性的题目,从昨晚到现在,不断写不断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;
}

以上为个人记录,供以后复习所用,如有错误请指出!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值