码迷,mamicode.com
首页 > 其他好文 > 详细

sgu111

时间:2014-11-26 11:36:13      阅读:164      评论:0      收藏:0      [点我收藏+]

标签: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

标签:sgu111   very simple problem   大整数开方   高精度计算   

原文地址:http://blog.csdn.net/tgop_knight/article/details/41493571

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!