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;
}