标签:sgu111 very simple problem 大整数开方 高精度计算
SGU111 Very Simple Problem
题目大意:
输入一个自然数N,找到一个平方不超过N的最大的整数。
输入:
输入文件包括一个数N(1≤N≤10^1000)。
输出:
答案
样例输入:
16
样例输出:
4
这还非常简单的题目。。。(对于代码能力强大的人而言确实如此)
问题就是对大整数进行开方。
方法的话有两种。
首先,对于K位数字的开方得到的结果一定是(k div 2)位(把数字看成a*10^k即可)
方法一:手工模拟开方。
从最高位向最低位每一位进行枚举,然后得到答案。
大致思路如下(ans[]为答案数字):
1.枚举第一位(因为不能为0)所以特别拿出来,当ans[]*ans[]>N时,停止枚举(枚举数字记得--)。
2.轮流枚举第二至第(K div 2)位,也是当ans[]*ans[]>N时停止枚举(同上)。
显然直接计算会超时,这里有一个优化:
对于当前枚举的第i位的数字b,令整个枚举的数字为a+b,则有:
当前数字平方=(a+b)*10^(2*(k-i))=(a^2+2*a*b+b^2)*10^(2*(k-i));
a^2可以提前处理,这样可以让时间复杂度下降一维,成功解决超时问题。
10^(2*(k-i))实际就是在后面添2*(k-i)个零。
方法二:高精度压位+二分枚举答案
应该很好理解。时间复杂度貌似也可以通过。
压位的话代码量可能有点大。
输出记得打前导零(因为压位)
代码参考博客(下面不会给出方法二的代码):(SGU111 高精度开方)
注意事项:
1.高精度计算的代码正确性(如‘/‘、‘%‘乱用等)。
2.单精度整数平方加法后要考虑变化的两位进行进位。
3.答案倒序输出。
下面附上方法一的代码:
#include <stdio.h> #include <stdlib.h> #include <math.h> #define SQR(a) ((a)*(a)) #define MAX(a,b) ((a)>(b)?(a):(b)) #define MIN(a,b) ((a)>(b)?(a):(b)) struct big { int a[1200]; }s,pre,sqrpre,ans,zero,one; int compare(struct big a,struct big b) //比较大整数a、b的大小关系 { int i,l=a.a[0]; if (a.a[0]>b.a[0]) return 1; else if (a.a[0]<b.a[0]) return -1; else { for (i=l;i>0;i--) if (a.a[i]>b.a[i]) return 1; else if (a.a[i]<b.a[i]) return -1; return 0; } } struct big change(struct big a,int b,int x) //计算大整数a*b*10^x(b为单精度整数) { int i,l=a.a[0]; struct big re; re=zero; for (i=1;i<=l;i++) { re.a[x+i]+=(a.a[i]*b); re.a[x+i+1]+=(re.a[x+i]/10); re.a[x+i]%=10; } while (re.a[x+l+1]>=10) { l++; re.a[x+l+1]+=re.a[x+l]/10; re.a[x+l]%=10; } while (re.a[x+l+1]>0) l++; re.a[0]=x+l; return re; } struct big count(struct big a,struct big b) //计算大整数a+b { int i,l=MAX(a.a[0],b.a[0]); struct big re; re=zero; for (i=1;i<=l;i++) { re.a[i]+=(a.a[i]+b.a[i]); re.a[i+1]+=(re.a[i]/10); re.a[i]%=10; } while (re.a[l+1]>=10) { l++; re.a[l+1]+=re.a[l]/10; re.a[l]%=10; } while (re.a[l+1]>0) l++; re.a[0]=l; return re; } void init() { int i,j,l; char c; c=getchar(); while ('0'<=c && c<='9') { one.a[++one.a[0]]=c-'0'; c=getchar(); } for (i=1;i<=one.a[0];i++) s.a[i]=one.a[one.a[0]-i+1]; s.a[0]=one.a[0]; ans.a[0]=(one.a[0]+1)/2; l=ans.a[0]; //计算答案长度 for (pre.a[ans.a[0]]=1;pre.a[ans.a[0]]<=9;pre.a[ans.a[0]]++) { sqrpre.a[2*(ans.a[0]-1)+1]=SQR(pre.a[ans.a[0]])%10; sqrpre.a[2*(ans.a[0]-1)+2]=SQR(pre.a[ans.a[0]])/10; if (sqrpre.a[2*(ans.a[0]-1)+2]==0) sqrpre.a[0]=2*(ans.a[0]-1)+1; else sqrpre.a[0]=2*(ans.a[0]-1)+2; if (compare(sqrpre,s)>0) break; } //枚举第一位 pre.a[ans.a[0]]--; sqrpre.a[2*(ans.a[0]-1)+1]=SQR(pre.a[ans.a[0]])%10; sqrpre.a[2*(ans.a[0]-1)+2]=SQR(pre.a[ans.a[0]])/10; if (sqrpre.a[2*(ans.a[0]-1)+2]==0) sqrpre.a[0]=2*(ans.a[0]-1)+1; else sqrpre.a[0]=2*(ans.a[0]-1)+2; pre.a[0]=ans.a[0]; ans=pre; //计算第一位的答案并做好预处理 for (i=l-1;i>0;i--) //枚举第i位 { for (ans.a[i]=1;ans.a[i]<=9;ans.a[i]++) { one=change(pre,2*ans.a[i],i-1); one=count(sqrpre,one); one.a[2*(i-1)+1]+=SQR(ans.a[i])%10; one.a[2*(i-1)+2]+=SQR(ans.a[i])/10; j=2*(i-1)+1; while (one.a[j]>=10) { one.a[j+1]+=one.a[j]/10; one.a[j]%=10; j++; } j=2*i; while (one.a[j]>=10) { one.a[j+1]+=one.a[j]/10; one.a[j]%=10; j++; } one.a[0]=MAX(one.a[0],j); if (compare(one,s)>0) break; } ans.a[i]--; one=change(pre,2*ans.a[i],i-1); one=count(sqrpre,one); one.a[2*(i-1)+1]+=SQR(ans.a[i])%10; one.a[2*(i-1)+2]+=SQR(ans.a[i])/10; j=2*(i-1)+1; while (one.a[j]>=10) { one.a[j+1]+=one.a[j]/10; one.a[j]%=10; j++; } j=2*i; while (one.a[j]>=10) { one.a[j+1]+=one.a[j]/10; one.a[j]%=10; j++; } one.a[0]=MAX(one.a[0],j); sqrpre=one; pre=ans; //计算第i位的答案 } for (i=ans.a[0];i>=1;i--) printf("%d",ans.a[i]); printf("\n"); return ; } int main() { init(); return 0; }
标签:sgu111 very simple problem 大整数开方 高精度计算
原文地址:http://blog.csdn.net/tgop_knight/article/details/41493571