欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

UVA 12333 - Revenge of Fibonacci (斐波那契的复仇) 【后日谈】by SuCicada

程序员文章站 2024-01-07 20:13:04
...

正篇以及正确解题思路和代码参见

UVA 12333 - Revenge of Fibonacci (斐波那契的复仇) by SuCicada

此篇为后日谈


要说为什么专门开一篇来记录想法呢,主要是因为想说的太多了。首先从UVA的提交记录上来看,上一次答题是在足足1年之前了。
这么久以来都没有再好好做算法,感觉快要忘本了。而且出来之后脑子也不是那么灵光了,虽然更理性,但是却少了些抽象的想象力。

再说这道题,一开始我根本不知道什么字典序,完全是打表暴力比对的。把前40位算出来那里都是没错的。
但是之后我想的是:用map排个序,然后二分查找。但是这样会有问题,因为二分找到的可能并不是最小的,所以找到之后还需要分别找到同样匹配前缀的那一区域的数字,因为是map排过序的,所以他们都是挨着的。

但是之后在极端痛苦了两个晚上之后,我放弃了这种做法,经过测试计算,发现我电脑中g++在一秒钟可以进行4亿次运算,假设有5万个40位数字需要比对,4亿除以5万除以40 有20万呢,或者保险点我们取个1000。(一直到当时我都以为这道题的时间限制是1秒)

然后我就在死灰复燃的状态下在下2天晚上把位数比对写出了(暴力)。为了减少比对量,我打表了前3位,也就是记录下前三位的数字第一次出现的序数,而且这个序数是map中的序数,map中的序数和fibo的序数可是不同的。
而且map不能随机读取,怎么办,我又创造了两个数组专门存放键和值。

后来我还发现个位数的搜索起来比较慢,因为他们要暴力比对的数是最多的,所以我又用一个map来记录已经查找过的结果。

我真的是要疯,一直到昨天晚上我把这套模型整通。本地一跑,通过,time测下来 1.2秒,我巨喜。提交,time limit exceeded。把udebug上的样例全部跑了一遍,没有超过1.5秒的。我随机生成各种5万个数的组合,2秒之内绝对能过。

但是 time limit exceeded, time limit exceeded,time limit exceeded,time limit exceeded,time limit exceeded。
”难道是表打的太小了?“我又将打表位数升到4,升到5。然而为什么更慢了。

那会已经3点钟了,我感到生命力的衰减,在极度无望的情况下,我打开百度,UVA 12333,我真特么被这道题23333了,

这道题考大数+字典树

我惊住,一个新的窗子仿佛打开了,一查,全懂了,完全懂了。我缩在床上颤抖不已,脑子瞬间就明白了我该怎么做,但是身体已经无法支撑下去,为了能保持住自己的生命力。我艰难的进入睡眠。

第二天我写,真的很快,因为我已经知道了,虽然遇到了oj离奇而严厉的 runtime error错误。但是我很冷静,因为我知道这个方法肯定是没错的,这条路一定是对的。就是这种知道目的的坚定。

当Accepted出现在屏幕上,我想哭了。太痛苦了,太刺激了,太疯狂了。再加上17个小时没有进食带来的虚弱感让我恍惚,感觉我活下来了。

劫后余生的感激之情。

而现在已经5点怅然若失感时不时在席卷我,跨时两个星期,4个夜晚的崩溃,20余小时的挣扎,20多次的错误。我太弱了,我为我的弱小感到伤心,但是我又为我的成功感到高兴。

算法拯救我,算法伤害我,算法摧残我,算法安慰我。

我时尝为我的自艾感到痛苦。

但是我害怕痛苦吗,不如说我正喜欢痛苦。

UVA 12333 - Revenge of Fibonacci (斐波那契的复仇) 【后日谈】by SuCicada

附赠更快的却更错的代码:

#include<iostream>
#include<algorithm>
#include<iterator>
#include<vector>
#include<string>
#include<cstdio>
#include<iomanip>
#include<map>
#include<cmath>
#include<cstring>
using namespace std;

vector<string> fibonacci(100005);
map<string,int> fiboSorted;
vector<string> fiboName(100005);
vector<int> fiboIndex(100005);
int limitSite = 52;

string add(string a,string b){
    int aLen = a.size()-1;
    int bLen = b.size()-1;
    int aSiteNum = a[limitSite>aLen?aLen:limitSite];
    int bSiteNum = b[limitSite>bLen?bLen:limitSite];
    int ten = 0;
    int newLen = bLen+1; // 新数长度,一位放头放0

    int i = aLen-1;
    int j = bLen-1;
    if(aLen == bLen && aSiteNum!=bSiteNum){ // 位数不等,并且数字已经取了部分了
        i--; // 小的数扔一位
    }

    string res(newLen+1,'0');  // 最后一位放位数(奇偶)
    int resSite = newLen-1;  // 记录结果的当前位数
    for(; j>=0; i--,j--){
        int aa='0';
        if(i>=0){
            aa = a[i];
        }
        int r = aa-'0' + b[j]-'0' + ten;
        ten = r/10;
        res[resSite--] = r%10+'0';
        // cout<<r<<" "<<ten<<" "<<res<<endl;
    }
    // if(plusSite){
    res[0] = ten+'0';
    // }
    if(ten==0){ //不会进位
        string result = res.substr(1,limitSite+1);
        result[result.size()-1] = bSiteNum;
        return result;
    }else{
    // if(aSiteNum!=bSiteNum){ // 位数不等
        string result = res.substr(0,limitSite+1);
        result[result.size()-1] = !(bSiteNum-'0') +'0';
        return result;
    }
}
vector<string> fibo(100005);
// int find(string str,int fbegin,int fend);

// map<int,map<int,map<int,int> > > dict;
int dict[1004];

int presitename = 3;    

int find(string str){
    int presitenum10 = pow(10,presitename);
    int threenum = 0;// = (name[0]-'0')*100 + (name[1]-'0')*10 + (name[2]-'0');
    int len = str.size(); // 这个长度很重要
    int nowSiteName = presitename; // min(len,presitename); // 考虑数字要更小的情况, 比如12 我们要存12的位置, 不能是120
    for(int j=0;j<nowSiteName;j++){
        if(len >= j+1){ // 加1是因为要比对数字字符长度
            threenum += (str[j]-'0') * pow(10,nowSiteName-1-j);
        }
    }

    int newSite = pow(10,presitename-min(len,presitename));  // 这个是为了确定查找的区间, 要使用的数量级和长度相关



    // cout<<threenum<<endl;
    int index = dict[threenum];
    int end = -1;
    int nextSite = threenum+newSite;
    if(dict[nextSite] == -1){
        nextSite += 5* newSite;
    }
    // while((end == -1) && (nextSite <= presitenum10)){  // 防止 dict下一位没有东西
    //     end = dict[nextSite];
    //     cout<<nextSite<<" "<<dict[nextSite]<<" "<<end<<endl;
    //     nextSite += newSite;
    // }

    if(nextSite > presitenum10){
        end = 100001;
    }else{
        end = dict[nextSite]; 
    }

    // cout<<index<<" "<<end<<endl;
    // 要找合适的,先从index开始一个一个数比较,
    // 一位一位的比,如果相同,就记录fibo序数,取小,一直比到不相等,跳出
    int startSame = 0;
    int minFiboIndex = 100001;
    for(int i=index;i<end;i++){
        if( str.size() <= fiboName[i].size()){
            string fs = fiboName[i].substr(0,str.size()); 
            // cout<<i<<" "<<str<<" "<<fs<<endl;
            if(str == fs){
                minFiboIndex = min(fiboIndex[i],minFiboIndex);
                startSame = 1;
            }else{
                if(startSame == 1){
                    break;
                }
            }
        }
    }
    if(minFiboIndex == 100001){
        minFiboIndex = -1;
    }
    return minFiboIndex;
}   

map<string,int> answer;
int run(){

    fibonacci[0] = fibonacci[1] = "11";
    fiboSorted["1"] = 0;
    for(int i=2;i<=100000;i++){
        fibonacci[i] = add(fibonacci[i-2],fibonacci[i-1]);
        string s = fibonacci[i].substr(0,fibonacci[i].size()-1);
        fiboSorted[s] = i;
        // cout<<fibonacci[i]<<" "<<i<<" "<<fiboSorted[fibonacci[i]]<<endl;
        // cout<<setw(2)<<i<<" ";
        // cout<<" "<<setw(limitSite+1)<<fibonacci[i].substr(0,fibonacci[i].size()-1)<<" ";
        // cout<<setw(4)<<fibonacci[i].substr(fibonacci[i].size()-1);
        // cout<<endl;
        // stirng aa =
        // cout<<" "<<setw(4)<<
        // cin.get();
    }

    // copy(fibonacci.begin(),fibonacci.end(),ostream_iterator<string>(cout," |\n"));
    // cout<<fibonacci[99999]<<endl;
    // cout<<endl;
    memset(dict,-1,sizeof(dict));

    int i=0;
    for(map<string,int>::iterator m = fiboSorted.begin();m!=fiboSorted.end();m++,i++){
        // cout<<m->first<<" "<<m->second<<endl;
        fiboName[i] = m->first;
        fiboIndex[i] = m->second;

        string name = m->first;
        int index = i;
        int threenum = 0;// = (name[0]-'0')*100 + (name[1]-'0')*10 + (name[2]-'0');
        int len = name.size(); // 这个长度很重要
        int nowSiteName = presitename; // min(len,presitename); // 考虑数字要更小的情况, 比如12 我们要存12的位置, 不能是120
        for(int j=0;j<nowSiteName;j++){
            if(len >= j+1){ // 加1是因为要比对数字字符长度
                threenum += (name[j]-'0') * pow(10,nowSiteName-1-j);
            }
        }

        // cout<<threenum<<" "<<dict[threenum]<<" "<<index<<endl;   
        if(dict[threenum] == -1){ // 未记录
            dict[threenum] = index; // 因为递增遍历 map 数列, 所以第一次填入的 map 序号肯定是最小的
        }

    }

    // for(int i=0;i<sizeof(dict)/sizeof(int);i++){
    //     printf("%03d %6d %s\n ",i,dict[i],fiboName[dict[i]].c_str());
    //         // cout<<i<<dict[i]<<endl;
    // }
    // makedict();


// return 0;
    // int mapLen = i;
    int T;
    cin>>T;
    for(int i=0;i<T;i++){
        string n;
        cin>>n;
        int res;
        if(answer.count(n)){
            res = answer[n];
        }else{
            res = find(n);
            answer[n] = res;
        }
        printf("Case #%d: %d\n", i+1, res);
        // cout<<"find(n)<<endl;

        // int res = find(n,0,mapLen);
        // cout<<fiboIndex[res]<<endl;
    }
    // cout<<fiboSorted.size()<<endl;
    // cout<<(fiboSorted.begin()+1)->first<<endl;
}



int main()
{
    run();
    return 0;
}
相关标签: uva.onlinejudge.org

上一篇:

下一篇: