zoukankan      html  css  js  c++  java
  • POJ 2109 Power of Cryptography二分+大数相乘和pow为什么可以直接过的原因

    http://poj.org/problem?id=2109

    这个题目一看就是个大数的题目,但是很流行下面这段一行代码的程序:

    #include<stdio.h>
    #include<math.h>
    #include<iostream>
    using namespace std;
    int main()
    {
    double a,b;
    while(cin>>a>>b)
    {
    cout<<pow(b,1.0/a)<<endl;
    }
    return 0;

    }

    虽然这段代码经过无数次提交ac的证明,但是我不得不说,不懂,完全不懂,double 能把100位的n按照合理精度转下来吗?、、

    也许这个代码能够ac,是因为测试数据不严谨吧。

    先介绍正解,然后在说明原因。

    下面,介绍正解:

    二分+大数运算

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<iostream>
    using namespace std;


    #define M 400
    #define mem0(f) memset(f,0,sizeof(f))
    //先写大数相乘
    char res[M],nn[M];
    int k;
    void times(char*a,char*b,char*s)
    {
    char aa[M],bb[M];
    mem0(aa);
    mem0(bb);
    int l1=strlen(a),l2=strlen(b);
    for(int i=0;i<l1;i++)
    {
    aa[i]=a[l1-i-1]-'0';
    }
    for(int i=0;i<l2;i++)
    {
    bb[i]=b[l2-1-i]-'0';
    }
    int n=0;
    for(int i=0;i<l1;i++)
    {
    n=i;
    for(int k=0;k<l2;k++)
    {
    s[n]+=aa[i]*bb[k];
    if(s[n]>9)
    {
    s[n+1]+=s[n]/10;
    s[n]=s[n]%10;
    }
    n++;
    }
    }
    if(s[n])n++;
    for(int i=0;i<n;i++)
    {
    s[i]+='0';
    }
    char t;
    for(int i=0;2*i<n;i++)
    {
    t=s[i];
    s[i]=s[n-1-i];
    s[n-i-1]=t;
    }
    }
    void power(char*a,int n,char*s)
    {
    char sm[M];
    strcpy(s,"1");
    for(int i=0;i<n;i++)
    {
    mem0(sm);
    //cout<<n<<endl;
    times(s,a,sm);
    strcpy(s,sm);
    //puts(s);
    }
    //strcpy(s,sm);
    }
    int cmp(char*a,char*b)
    {
    int l1=strlen(a),l2=strlen(b);
    if(l1>l2)return 1;
    else if(l1<l2)return -1;
    else
    {
    for(int i=0;i<l1;i++)
    {
    if(a[i]>b[i])return 1;
    else if(a[i]<b[i])return -1;
    }
    return 0;
    }
    }
    int minn,maxx,n,ok,cen;
    char mid[M];
    int main()
    {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    while(~scanf("%d%s",&k,nn))
    {
    ok=0;
    mem0(mid);
    mem0(res);
    n=ceil(((float)strlen(nn)/k));
    //puts(nn);
    minn=(int)pow(10,n-1);
    maxx=(int)pow(10,n)-1;
    //cen=(minn+maxx)/2;
    while(!ok)
    {
    mem0(res);
    mem0(mid);
    cen=(minn+maxx)/2;
    sprintf(mid,"%d",cen);
    power(mid,k,res);
    if(cmp(res,nn)<0)
    {
    minn=cen+1;
    }
    else if(cmp(res,nn)>0)
    {
    maxx=cen-1;
    }
    else
    {
    ok=1;
    }
    if(minn>maxx)break;
    }

    //这个题目说总会存在k是逗我们玩的,其实就是找满足k的p次方小于或等于n的最大k值,这也是为什么pow可以直接过的原因之一。

    //如果没有下面在无解时的近似处理,结果是WA,实验过了,加上这段代码的话就可以AC,果断测试数据坑人有木有!
    if(!ok)
    {
    while(cmp(res,nn)>0)
    {
    cen--;
    sprintf(mid,"%d",cen);
    mem0(res);
    power(mid,k,res);
    }
    }
    printf("%d ",cen);
    }
    return 0;
    }

    上面对代码的说明解释了pow可以直接过的部分原因:反正是要求一个最大值,根本就不需要存储所有的精度。

    深层分析如下

    首先,题目中的数据强度并不弱,这一点确实如题目中所说:“For all such pairs 1<=n<= 200, 1<=p<10101,所以,double型是不能精确地表示出所给数据,但是却能表示出一个近似值。

    当向double型变量中存入

    4357186184021382204544

    然后再输出,得到的是

    4357186184021382000000

    后六位的值变为了0,这一点和int型变量是有很大区别的。也就是说当存入double型变量的值超出了它的精度表示范围时,将低位的数据截断。(关于浮点数在计算机中的表示方法,百度吧…讲的蛮清楚的。)

    在本题中,如果测试数据为:

    7     4357186184021382204544

    实际上所处理数据是:

    7     4357186184021382000000

    拿4357186184021382000000开7次方的结果自然就是1234。

    为什么不是1233或者1235呢?

    12337=4332529576639313702577

    12347=4357186184021382204544

    12357=4381962969567270546875

    可以看出在double型所能表示的精度范围内,它们三个值已经不同了。

    所以,此题中的测试数据也都是类似于上述情况,所以才能使用double型开n次方的方法。

    关于此,感谢

    CSDN的PangWD_C
    更多及原文请访问http://blog.csdn.net/code_pang/article/details/8263971
  • 相关阅读:
    cocos2dx打包apk
    cocos2d 小游戏
    排序算法笔记二
    把一张合成图分拆出各个小图
    出栈入栈动画demo
    Android 面試題
    AS项目删减打包-01
    c程序指针题
    ubuntu14.04 设置默认登录用户为root
    Ubuntu14.04 Java环境变量配置
  • 原文地址:https://www.cnblogs.com/plank-george-zzo/p/3221306.html
Copyright © 2011-2022 走看看