信息学奥赛一本通(C++版) 第二部分 基础算法 第一章 高精度计算

来源:互联网 发布:linux中的telnet命令 编辑:程序博客网 时间:2024/06/03 12:47

信息学奥赛一本通(C++版) 第二部分 基础算法 第一章 高精度计算

http://ybt.ssoier.cn:8088/

//1307 【例1.3】高精度乘法
//手动模拟乘法运算
//提交,测试点5,答案错误,猜测,应该是0的情况,没考虑
//提供一组测试数据
//输入:
//123
//0
//输出:
//0
//考虑了0的情况,修改,提交AC 2017-11-9
//编到这里,感觉高精度加是高精度算法的基础
#include <stdio.h>
#include <string.h>
int a[110],b[110],c[220];
char s[110];
void mul(){
    int i,j;
    for(i=1;i<=a[0];i++)
        for(j=1;j<=b[0];j++){
            c[i+j-1]+=a[i]*b[j];
            c[i+j]+=c[i+j-1]/10;
            c[i+j-1]%=10;
        }
    c[0]=a[0]+b[0]-1;
    if(c[c[0]+1]>0)c[0]+=1;
}
int main(){
    int i,len,k;
    memset(a,0,sizeof(a)),memset(b,0,sizeof(b)),memset(c,0,sizeof(c));
    scanf("%s",s);
    len=strlen(s),k=0;
    for(i=len-1;i>=0;i--)a[++k]=s[i]-'0';
    a[0]=len;
    
    scanf("%s",s);
    len=strlen(s),k=0;
    for(i=len-1;i>=0;i--)b[++k]=s[i]-'0';
    b[0]=len;
    
    if((a[0]==1&&a[1]==0)||(b[0]==1&&b[1]==0))printf("0");
    else{
        mul();
        for(i=c[0];i>=1;i--)printf("%d",c[i]);
    }
    return 0;
}

//1308 【例1.5】高精除
//基本可以猜到测试数据
//1 分子为0 商为0,余数为0
//2 分子 分母相等,商为1,余数为0
//3 在long long 范围内的分子 分母
//以上三点至少可得30分
//如果是比赛,上述观点能得部分分
//基本想法是用高精度减,但容易超时
//换思路,
//此文代码写得比较短https://www.cnblogs.com/shenben/p/5878072.html
//学习
//算法明白后,编写代码,但调试了70分钟,样例通过后,提交 2017-11-10
//提交,测试点3,答案错误
//提供一组测试样例
//输入:
//1000
//1
//输出:
//1000
//0
//此题编写,感觉自个代码在到处打补丁,很不顺畅。
//修改,提交AC,不过,呢,代码全程都是自个编下来的,在编写过程中再也没有参考其他,也算是进步吧。
//日后此题应该还会再编写一遍。
#include <stdio.h>
#include <string.h>
int a[310],b[310],c[310],d[310],t[310];//d商 t临时数组
char s[310];
int compare(int a[],int b[]){//a>b 1 a==b 0 a<b -1 高精度 比较大小
    int i;
    if(a[0]>b[0])return 1;
    if(a[0]<b[0])return -1;
    if(a[0]==b[0])
        for(i=a[0];i>=1;i--)
            if(a[i]>b[i])return 1;
            else if(a[i]<b[i])return -1;
    return 0;
}
void sub(int a[],int b[]){//a>b 高精度减
    int i;
    for(i=1;i<=b[0];i++){
        if(a[i]<b[i]){//借位
            a[i]+=10;
            a[i+1]--;
        }
        a[i]-=b[i];
    }
    i=a[0];
    while(a[i]==0)i--;
    if(i==0)a[0]=1;//此句写成if(a[0]==0)a[0]=1;//漏了此句
    else  a[0]=i;    
}
int main(){
    int i,len,k,j,q;
    
    memset(a,0,sizeof(a)),memset(b,0,sizeof(b)),memset(d,0,sizeof(d));
    
    scanf("%s",s);
    len=strlen(s),k=0;
    for(i=len-1;i>=0;i--)a[++k]=s[i]-'0';
    a[0]=len;
    
    scanf("%s",s);
    len=strlen(s),k=0;
    for(i=len-1;i>=0;i--)b[++k]=s[i]-'0';
    b[0]=len;
    
    if(a[0]==1&&a[1]==0)printf("0\n0");//0初一其它数的处理
    else if(compare(a,b)==0)printf("1\n0");//a==b
    else if(compare(a,b)==-1){//a<b
        printf("0\n");
        for(i=a[0];i>=1;i--)printf("%d",a[i]);
    }else{//a>b
        d[0]=a[0]-b[0]+1,q=0;//商的位数
        for(i=a[0];i>=1;i--){
            if(compare(a,b)<=0)break;//此行写成if(compare(a,b)==-1)break;//添加此行
            memset(t,0,sizeof(t)),t[0]=i,k=t[0];//此处写成t[0]=a[0],//此处写成k=0//漏了该句 t[0]=a[0];
            for(j=b[0];j>=1;j--)t[k]=b[j],k--;//此处写成t[i+k]=b[j],k++;//此处写成 for(j=b[0];j>=1;j--)t[i+k]=b[j],k++;
            while(compare(a,t)>=0)d[d[0]-q]++,sub(a,t);//此处写成while(compare(a,t)==1)//此处写成 while(compare(a,b)==1)
            q++;
        }
        i=d[0];
        while(d[i]==0)i--;//前导0的处理
        d[0]=i;
        for(i=d[0];i>=1;i--)printf("%d",d[i]);
        printf("\n");
        for(i=a[0];i>=1;i--)printf("%d",a[i]);
    }
    return 0;
}

//1309 【例1.6】回文数(Noip1999)
//高精度加算法,很有信心
//后判断
//翻转
//先加
//30步跳出
//仔细看题,发现并不只是十进制,是N进制,马上意识到该题的难度了
//过了一晚之后,再想想,只要把十进制中的10换成N,并编写一个打印函数,打印位上的值大于等于10的 即可。
//题目没有明确位数个数,故数组均开到500
//样例通过,提交,未通过,
//因是历年NOIP普及组 复赛试题,容易得到测试数据
//看了看,确实会比较难过,因为输入数据中就有16进制
//马上对读取后的数据进行修改处理,题中没有说输入数据是否大写,或是小写,一并处理了
//再次提交,全未通过,查看原始输入输出数据
//查了 洛谷 P1015 回文数
//https://www.luogu.org/problemnew/show/1015
//洛谷上通过后,再进行提交,AC,
//http://ybt.ssoier.cn:8088中该题,没法看,直接参考洛谷里的题目,按里面的输入输出要求进行提交
//2017-11-10
#include <stdio.h>
#include <string.h>
int N,a[500],b[500],step=0;
char s[500];
void add(int a[],int b[]){
    int i;
    for(i=1;i<=a[0];i++){
        a[i]+=b[i];
        a[i+1]+=a[i]/N;
        a[i]%=N;
    }
    if(a[a[0]+1]>0)a[0]+=1;
}
int judge(int a[]){//0非回文,1回文
    int i;
    for(i=1;i<=a[0]/2;i++)
        if(a[i]!=a[a[0]-i+1])
            return 0;
    return 1;
}
void overturn(int a[]){
    int i,j=0;
    b[0]=a[0];
    for(i=a[0];i>=1;i--)
        b[++j]=a[i];//此处写成b[j]=a[i],j++;
}
void print(int a[]){
    int i;
    for(i=a[0];i>=1;i--)
        printf("%d",a[i]);
    printf("\n");
}
int main(){
    int len,i,k=0;
    scanf("%d%s",&N,s);
    len=strlen(s);
    for(i=len-1;i>=0;i--)//重写读入数据的处理
        if('0'<=s[i]&&s[i]<='9')
            a[++k]=s[i]-'0';
        else if('a'<=s[i]&&s[i]<='z')
            a[++k]=s[i]-'a'+10;
        else if('A'<=s[i]&&s[i]<='Z')
            a[++k]=s[i]-'A'+10;
    a[0]=len;
    while(judge(a)==0){
        //printf("1 "),print(a);
        overturn(a);
        //printf("2 "),print(b);
        add(a,b);
        //printf("3 "),print(a);
        step++;
        if(step==31)break;
    }
    
    if(step==31)printf("Impossible!");//此处写成 printf("Impossible");
    else printf("STEP=%d",step);//此处写成 printf("%d",step);
    return 0;
}


//1168 大整数加法
//AC 2017-11-7 23:00
#include <stdio.h>
#include <string.h>
int a[210],b[210],c[210];
char s[210];
void add(int a[],int b[],int c[]){
    int i;
    c[0]=a[0]>b[0]?a[0]:b[0];
    for(i=1;i<=c[0];i++){
        c[i]+=a[i]+b[i];
        c[i+1]=c[i]/10;//进位
        c[i]%=10;
    }
    if(c[c[0]+1]>0)c[0]=c[0]+1;
}
int main(){
    int len,i,k;
    memset(a,0,sizeof(a)),memset(b,0,sizeof(b)),memset(c,0,sizeof(c));
    scanf("%s",s);
    len=strlen(s),k=0;
    for(i=len-1;i>=0;i--)
        a[++k]=s[i]-'0';
    i=k;
    while(a[i]==0)i--;
    a[0]=i;//整数a的长度 a[0]存储长度
    
    scanf("%s",s);
    len=strlen(s),k=0;
    for(i=len-1;i>=0;i--)
        b[++k]=s[i]-'0';
    i=k;
    while(b[i]==0)i--;
    b[0]=i;//整数b的长度 b[0]存储长度
    
    add(a,b,c);
    for(i=c[0];i>=1;i--)printf("%d",c[i]);
    return 0;
}


//1169 大整数减法
//先逐位相减,之后考虑退位,再考虑前导0的处理
//样例通过,提交AC,和明显感觉此次大减数减法编写得要好,要有心得,熟能生巧,此次算法发有相当一部分是自创。
#include <stdio.h>
#include <string.h>
int a[210],b[210],c[210];
char s[210];
void sub(){
    int i;
    c[0]=a[0];
    for(i=1;i<=a[0];i++)c[i]=a[i]-b[i];//先逐位相减,此时c[i]可是是负数
    for(i=1;i<=c[0];i++)//之后考虑退位,经减法运算检验,前一位,最多退位1位
        if(c[i]<0){
            c[i]+=10;
            c[i+1]-=1;
        }
    i=c[0];//再考虑前导0的处理
    while(c[i]==0)i--;
    c[0]=i;
}
int main(){
    int i,len,k;
    memset(a,0,sizeof(a)),memset(b,0,sizeof(b));
    scanf("%s",s);
    len=strlen(s);
    k=0;
    for(i=len-1;i>=0;i--)a[++k]=s[i]-'0';//此处写成 for(i=len-1;i>=0;i--)a[++k]=s[i];有失水准
    a[0]=len;
    
    scanf("%s",s);
    len=strlen(s);
    k=0;
    for(i=len-1;i>=0;i--)b[++k]=s[i]-'0';//此处写成 for(i=len-1;i>=0;i--)b[++k]=s[i];有失水准
    b[0]=len;
    
    sub();
    for(i=c[0];i>=1;i--)printf("%d",c[i]);
    return 0;
}


//1170 计算2的N次方
#include <stdio.h>
#include <string.h>
int a[110];
void f(){//a[0]存储数据长度
    int i;
    for(i=1;i<=a[0];i++)a[i]*=2;
    for(i=1;i<=a[0];i++){
        a[i+1]+=a[i]/10;
        a[i]%=10;
    }
    if(a[a[0]+1]>0)a[0]+=1;
}
int main(){
    int n,i;
    memset(a,0,sizeof(a));
    scanf("%d",&n);
    a[0]=1,a[1]=1;
    for(i=1;i<=n;i++)f();
    for(i=a[0];i>=1;i--)printf("%d",a[i]);
    return 0;
}


//1171 大整数的因子
//提交,未通过,测试点1,5,8,9答案错误
//重新看题,发现是非负整数,故0也可以,提供一组测试数据
//输入:
//0
//输出:
//2 3 4 5 6 7 8 9
//但发现程序测试下来没问题,
//在读题目,发现“若没有这样的k,则输出"none"。”,确实没注意到这句
//马上修改,提交AC
#include <stdio.h>
#include <string.h>
int a[50],b[50];
char c[50];
int divide(int k){
    int i=a[0],d=0;
    while(i>0)
        d*=10,d+=a[i],d%=k,i--;
    return d;
}
int main(){
    int i,len,k=0,flag=0;
    scanf("%s",c);
    len=strlen(c);
    for(i=len-1;i>=0;i--)a[++k]=c[i]-'0';
    a[0]=len;
    for(i=2;i<=9;i++)
        if(divide(i)==0)flag=1,printf("%d ",i);
    if(flag==0)printf("none");
    return 0;
}



//1172 求10000以内n的阶乘
//会乘法的高精度算法即可
//猜测数组位数需要开到10^6,具体等编好再测试
//经测试有35660位,故数组开到10^5
//样例通过,提交,未通过,所有数据测试点均答案错误
//觉得挺失望的,一测试,发现输出数据中右测试数据信息,马上删除,提交AC。
//编写程序,失望,希望,画风转变很快。
#include <stdio.h>
#include <string.h>
int a[100000];
void mul(int n){
    int i;
    for(i=1;i<=a[0];i++)a[i]*=n;
    for(i=1;i<=a[0];i++){
        a[i+1]+=a[i]/10,a[i]%=10;
    }
    if(a[i]>0){//首位处理
        while(a[i]>10){
            a[i+1]+=a[i]/10,a[i]%=10,i++;
        }
        a[0]=i;
    }
}
int main(){
    int n,i;
    memset(a,0,sizeof(a));
    scanf("%d",&n);
    a[0]=1,a[1]=1;
    for(i=1;i<=n;i++)mul(i);
    for(i=a[0];i>=1;i--)printf("%d",a[i]);
    return 0;
}


//1173 阶乘和
//基本思路,高精度乘,高精度加
//此题没有说明数据位数,暂且定在500位
//样例通过,提交,测试点3,4,5,6,7,9答案错误
//跟踪了代码,发现最高位发生了溢出。
//发现n<=50,故将数据位数改成100位
//提交,测试点3,4,6,7答案错误
//苦寻测试数据未果,发现 洛谷 P1009 阶乘之和 正是该题,
//在洛谷里提交AC,共4个测试数据,
//在自个文档中找到这4组测试数据,进行检验,发现,没问题,基本猜测测试数据n>50
//重新将数组位数从100改成500,提交AC,实在是没想法,竟然篡改数据,问不符题。
//总结,总体感觉水平有了长足的进步,代码敲下来,还是很轻松的。
#include <stdio.h>
#include <string.h>
int a[500],sum[500];// 写成int a[100],sum[100];测试点3,4,6,7答案错误
void mul(int n){
    int i;
    for(i=1;i<=a[0];i++)a[i]*=n;//各个位相乘
    for(i=1;i<=a[0];i++){//进位处理
        a[i+1]+=a[i]/10;
        a[i]%=10;
    }
    i=a[0];//多出位数处理
    while(a[i+1]>0)i++;
    a[0]=i;
    i=a[0];
    while(a[i]>10)a[i+1]+=a[i]/10,a[i]%=10,i++;//最高位处理,解决测试点3,4,5,6,7,9
    a[0]=i;
}
void add(){
    int i;
    sum[0]=sum[0]>a[0]?sum[0]:a[0];
    for(i=1;i<=sum[0];i++){
        sum[i]+=a[i];
        sum[i+1]+=sum[i]/10;
        sum[i]%=10;
    }
    if(sum[sum[0]+1]>0)sum[0]+=1;
}
int main(){
    int n,i;
    memset(a,0,sizeof(a)),memset(sum,0,sizeof(sum));
    scanf("%d",&n);
    a[0]=1,a[1]=1,sum[0]=1,sum[1]=0;
    for(i=1;i<=n;i++){
        mul(i);
        add();
    }
    for(i=sum[0];i>=1;i--)printf("%d",sum[i]);
}


//1174 大整数乘法
//将 1307 【例1.3】高精度乘法 代码拷贝过来,略做修改,提交AC,过程详见1307 【例1.3】高精度乘法 分析过程

//http://blog.csdn.net/mrcrack/article/details/78473644

//2017-11-9 AC
#include <stdio.h>
#include <string.h>
int a[210],b[210],c[420];
char s[110];
void mul(){
    int i,j;
    for(i=1;i<=a[0];i++)
        for(j=1;j<=b[0];j++){
            c[i+j-1]+=a[i]*b[j];
            c[i+j]+=c[i+j-1]/10;
            c[i+j-1]%=10;
        }
    c[0]=a[0]+b[0]-1;
    if(c[c[0]+1]>0)c[0]+=1;
}
int main(){
    int i,len,k;
    memset(a,0,sizeof(a)),memset(b,0,sizeof(b)),memset(c,0,sizeof(c));
    scanf("%s",s);
    len=strlen(s),k=0;
    for(i=len-1;i>=0;i--)a[++k]=s[i]-'0';
    a[0]=len;
    
    scanf("%s",s);
    len=strlen(s),k=0;
    for(i=len-1;i>=0;i--)b[++k]=s[i]-'0';
    b[0]=len;
    
    if((a[0]==1&&a[1]==0)||(b[0]==1&&b[1]==0))printf("0");
    else{
        mul();
        for(i=c[0];i>=1;i--)printf("%d",c[i]);
    }
    return 0;
}


//1175 除以13
//样例通过,提交AC
//总结,先会手动模拟,之后才容易编码
#include <stdio.h>
#include <string.h>
int a[110];
char s[110];
int main(){
    int i,len,k=0,b=0,c;
    
    scanf("%s",s);
    len=strlen(s);
    for(i=len-1;i>=0;i--)a[++k]=s[i]-'0';
    a[0]=len;
    
    i=a[0];//首项处理
    while(b<13)b*=10,b+=a[i],i--;
    
    printf("%d",b/13);
    b%=13,c=b;
    
    while(i>=1){
        b*=10,b+=a[i],i--;//此处写成b*=10,b+a[i],i--;,笔误坏事
        printf("%d",b/13);
        b%=13,c=b;
    }
    printf("\n%d",c);
    return 0;
}

2017-11-10  AC该章节内容


阅读全文
0 0