编程之美--只考加法的面试题

    技术2022-05-20  55

    看见题目的第一反应是DP,可以使用之前的值来计算,递推公式如下:f(n) = f(n-i)+i

    再增加适当的剪枝即可实现,但是对于64位的整数,肯定没有足够的存储空间,放弃。

     

    考虑之后对公式进行了推导:

     

    num = Σi(bg<=i<=end)=(bg+end)(end-bg+1)/2

     

    上式中的bg和end分别为求和公式中起始的位置和结束的位置,均为闭区间。

    令sub = end-bg+1,add = bg+end

    则上述等式转换为num=sub*add/2,原命题也就转换为求解合适的区间问题。

    得到了这个公式之后,再进行求解就会容易很多。

     

    再考虑到sub不能大于add等信息,可以用来剪枝,最后的N^2复杂度的代码如下:

    //只考加法的面试题 #include <iostream> #include <cmath> using namespace std; #define _DEBUG inline bool IsEven(const int &num) { return !( num&(0x01) ); } inline void PrintResult(int num,int cnt,int bg,int end) { cout<<"第"<<cnt<<"组组合为/n"<<num<<" = "<<bg<<"+...+"; // for (int i = bg;i<end;++i) // cout<<i<<"+"; cout<<end<<endl; } //we can obtain the eq. //num = (e-b+1)(e+b)/2 //then use n^2 algorithm to find suitable //e and b void Slove(int &num) { int sub = 2,//e-b+1 add = 2,//e+b boundery = 0,//up-boundery cnt =0; if (IsEven(num)) {//sub begin form 3, cuz two numbers cant obtain even sum sub =3; add =3; } num<<=1; for (; sub<=add; ++sub) { add = sub; for (; boundery<=num && add<=num;++add) { boundery = (sub*add); if( boundery == num ){ int bg = ((add-sub+1)/2), end = ((sub+add-1)/2); if(!bg || bg == end || add!= bg+end) continue; ++cnt; PrintResult(num>>1,cnt,bg,end); break; } } boundery = 0; } if(!cnt) cout<<"Not Found!"<<endl; } int main() { int num; while (cin>>num) { Slove(num); } return 0; }

     

    改程序在小数据的测试下正确,对于大数据没有测试。

     

    因为算法本身还是存在缺陷,使用N^2的算法当N很大的时候是非常慢的,这个问题如何解决呢?

    其实看过输出的结果之后就会有具体的思路了:

    按照把整数分为n个数相加的情况,对n进行遍历,但是这里仍然需要对n的范围进行求和,这里可以使用求和公式求解,因此该算法是O(N)的。

    新的算法如下:

    //只考加法的面试题 #include <iostream> #include <cmath> using namespace std; #define _DEBUG inline bool IsEven(const int &num) { return !( num&(0x01) ); } inline bool IsOdd(const int &num) { return (num&(0x1) ); } inline void PrintResult(int num,int n,int cnt,int bg,int end) { cout<<"第"<<cnt<<"组组合由"<<n<<"个数字相加得到/t"<<num<<" = "<<bg<<"+...+"; // for (int i = bg;i<end;++i) // cout<<i<<"+"; cout<<end<<endl; } inline int Sum(int bg,int end) { return (bg+end)*(end-bg+1); } //we can obtain the eq. //num = (e-b+1)(e+b)/2 //then use n^2 algorithm to find suitable //e and b void Slove2(const int &num) { int n =3;//num为奇数至少为两个数相加,偶数至少为三个数 int cnt = 0; if(IsOdd(num)) { if(num<<1 == Sum(num/2,num/2+1)) { ++cnt; PrintResult(num,2,cnt,num/2,num/2+1); } } int upper = (num/n+1)>3?(num/n+1):3; for (int i = n; i<=upper; ++i) { int bg = ceil( ( (double)num/i ) - (i/2.0) ); int end = floor(( ( (double)num/i ) + (i/2.0) )); if(!bg) break; if(end-bg+1 != i) continue; if(num<<1 == Sum(bg,end)) { ++cnt; PrintResult(num,i,cnt,bg,end); } } if(!cnt) cout<<"Not Found!"<<endl; } int main() { int num; while (cin>>num) { Slove2(num); } return 0; }

     

    新的算法在小数据下测试正确,如果有问题,欢迎提出讨论。


    最新回复(0)