poj 1001

懒了很久终于又来刷题,还从最简单的开始,暴露出不少问题。

本来是个最简单的问题,大数高精度计算,因为种种原因居然写了两个小时才AC。菜到家了难过

题目的描述:

Description

Problems involving the computation of exact values of very large magnitude and precision are common. For example, the computation of the national debt is a taxing experience for many computer systems. 

This problem requires that you write a program to compute the exact value of R n where R is a real number ( 0.0 < R < 99.999 ) and n is an integer such that 0 < n <= 25.

Input

The input will consist of a set of pairs of values for R and n. The R value will occupy columns 1 through 6, and the n value will be in columns 8 and 9.

Output

The output will consist of one line for each line of input giving the exact value of R^n. Leading zeros should be suppressed in the output. Insignificant trailing zeros must not be printed. Don't print the decimal point if the result is an integer.

Sample Input

95.123 12
0.4321 20
5.1234 15
6.7592  9
98.999 10
1.0100 12

Sample Output

548815620517731830194541.899025343415715973535967221869852721
.00000005148554641076956121994511276767154838481760200726351203835429763013462401
43992025569.928573701266488041146654993318703707511666295476720493953024
29448126.764121021618164430206909037173276672
90429072743629540498.107596019456651774561044010001
1.126825030131969720661201

第一眼看上去挺简单的,不就是用数组算嘛,连题都没看完,很快就写好代码了。读入一个float,一个int,算出float的小数位数,然后把float存入数组,计算结果,输出结果。提交,WA。

很不服气地拿sample一比较,发现5.1234 15居然错了。其实之前就意识到这个问题了,只是抱着侥幸心理忽略了:C++的浮点数不精确。5.1234被当成5.123999还是5.1234001来着,位数一大肯定错了。

改成string,因为不熟悉C++,又在strlen和string.length和string.length()之间折腾了一会儿,最后一个才是对的,strlen的参数必须是const char*。提交,依然WA。

仔细一对比,发现纯小数前面的0要省略。改过来,提交,WA。

再仔细一对比,发现小数最后的无意义的0要省略。改过来,提交,WA。

这次没有参照了。自己想想吧,试着输入整数,发现后面跟了小数点。改过来,我已经开始心虚了,提交,WA。

还是没有放弃,想了想还有什么特例。输入1.00,发现这个情况没处理,输出为1.00。改过来,提交,忐忑万分,果然没失望还是WA。

继续想想,试着输入0.000,发现这个情况没考虑,输出了0.000 。改过来,我简直不能相信过不了。最后依然是WA。

我已经被折磨得不行了,决定从网上找测试数据。

然后果然又发现两个问题,一个大问题是前后的省略0没处理好导致10.000 1这样的数据会被处理成1输出。一个小问题是 0 0这样的输入会输出成0 。

把这两个问题解决掉,才终于AC了。实在没想到简单的一道题能做成这样。代码看起来丑陋不堪,效率也非常低下,非常伤心,整理之后如下:

// Problems.cpp : 定义控制台应用程序的入口点。
//

#include <iostream>
#include <string>
#include <math.h>

using namespace std;
const int LENGTH = 600;

void calBigInt(int nums[LENGTH], int n){
	
	int tmp[LENGTH];
	int tmpResult[LENGTH];
	int tlength, length = 0;
	while(nums[length]>=0)length++; //length of nums
	memcpy(tmpResult,nums,sizeof(int) * LENGTH);
	for(int i = 0; i < n-1; i ++)
	{
		memset(tmp,-1,sizeof(int) * LENGTH);
		tlength = 0;
		while(tmpResult[tlength]>=0)tlength++; //length of tmpResult
		
		for(int j = 0; j < length; j++)//the jth number multiple with the tmp result
		{
			for(int k = 0; k < tlength; k++)
			{
				if(tmp[j+k] == -1)tmp[j+k] = 0;
				tmp[j+k] += nums[j] * tmpResult[k];
				if(tmp[j+k] >= 10)
				{
					if(tmp[j+k+1] == -1)tmp[j+k+1]=0;
					tmp[j+k+1] += tmp[j+k]/10;
					tmp[j+k] = tmp[j+k]%10;
				}
			}
		}
		tlength = 0;
		while(tmp[tlength]>=0)tmpResult[tlength] = tmp[tlength++];
	}
	memcpy(nums,tmpResult,sizeof(int) * LENGTH);
}

int main()
{
    string s;
	int n,t,length,start;
	int nums[LENGTH];
	while(cin >> s >> n)
    { 
		if(n == 0){
			cout<<1<<endl;continue;
		}
		
		///init of params
		memset(nums,-1,sizeof(int) * LENGTH);//init 
		t = 0;			//float count
		length = 0;		//be used as index
		
		int i = s.length() -1;
		while(i>=0)		  //transfer string to array
		{
			if(s[i]=='.'){// position of dot
				t = s.length() - 1 - i;
				i--;
				continue;
			}
			nums[length++] = s[i--] - '0';
		}

		t *= n;				//final float count
		calBigInt(nums,n);	//calculation

		///handle prefix 0 and suffix 0
		length = 0;
		while(nums[length]>=0)length++; 
		while(length >= 1 && nums[length-1]==0)length--;//length of effective number
		if(length == 0){
			cout << 0 << endl;continue;
		}
		start = 0;
		if(t > 0)//suffix 0
		{
			for(i = 0; i < t; i++)
			{
				if(nums[i]==0)continue;
				break;
			}
			start = i;
		}

		if(t >= length)//prefix 0 of pure decimal
		{
			cout<<".";
			for(i = 0; i < t-length; i++)
			{
				cout<<"0";
			}
		}
		
		for(i = length-1; i >= start; i --)
		{
			cout<<nums[i];
			if(i == t && t > start)cout<<".";
		}
		cout<<endl;
	}
	
    return 0;
}

但是我记得有一个更高效的计算幂的方法,矩阵幂运算。

比如要计算3^5 ,result = 1,先算tmp=3^(2^0)=3,5%2=1,所以result*=tmp=3, 然后3^(2^1) = 9, 5>>1=2%2=0, 什么也不干;然后3^(2^2)=81, 2>>1=1%2=1, 所以result*=tmp=243。这个方法可以极大地提高效率。试一试,把算法换成:

/**
 * Calculate the two numbers and save result to the first
 */
void CalTwoBigInt(int number1[LENGTH], int number2[LENGTH]){
	int length1 = 0, length2 = 0, length = 0;
	int result[LENGTH];
	while(number1[length1]>=0)length1++; //length of num1
	while(number2[length2]>=0)length2++; //length of num2
	memset(result, -1, LENGTH * sizeof(int));
	for(int j = 0; j < length2; j++)     //every number of num2 multiple with number1
	{
		for(int i = 0; i < length1; i++)
		{
			if(result[i+j] == -1)result[i+j] = 0;
			result[i+j] += number1[i] * number2[j];
			if(result[i+j]>=10)
			{
				if(result[i+j+1] == -1) result[i+j+1] = 0;
				result[i+j+1] += result[i+j]/10;
				result[i+j] %= 10;
			}
		}
	}
	memcpy(number1,result,LENGTH * sizeof(int));
}

void QuickCalBigInt(int nums[LENGTH], int n){
//	if(n < 0) throw exception("n must be a positive number.");
	if(n <= 0){
		memset(nums,0,LENGTH * sizeof(int));return;
	}
	if(n == 1)return;

	int num2[LENGTH], result[LENGTH];
	memset(result, -1, LENGTH * sizeof(int));
	memcpy(num2,nums,LENGTH * sizeof(int));
	result[0] = 1;
	while(true)
	{
		if(n%2 == 1)
		{
			CalTwoBigInt(result,num2);
		}
		n >>= 1;
		if(n == 0)break;
		CalTwoBigInt(num2, num2);
	}
	memcpy(nums,result,LENGTH * sizeof(int));
}

结果反而慢了。这个结果不能接受。自己测了几遍,发现只有当数据比较大的时候,后者才有略微的优势,大概也就提高了一倍的效率。

真是备受打击。


附:测试数据

输入:

99.001  3
99.010  1
99.010  3
99.100  1
99.100  3
95.123 12
0.4321 20
5.1234 15
6.7592  9
98.999 10
1.0100 12
.00001  1
.12345  1
0001.1  1
1.1000  1
10.000  1
000.10  1
000000  1
000.00  1
.00000  0
000010  1
000.10  1
0000.1  1
00.111  1
0.0001  1
0.0001  3
0.0010  1
0.0010  3
0.0100  1
0.0100  3
0.1000  1
0.1000  3
1.0000  1
1.0000  3
1.0001  1
1.0001  3
1.0010  1
1.0010  3
1.0100  1
1.0100  3
1.1000  1
1.1000  3
10.000  1
10.000  3
10.001  1
10.001  3
10.010  1
10.010  3
10.100  1
10.100  3
99.000  1
99.000  3
99.001  1
99.001  3
99.010  1
99.010  3
99.100  1
99.100  3
99.998  1
99.998  3
95.123 12
0.4321 20
5.1234 15
6.7592  9
98.999 10
1.0100 12
.00001  1
.12345  1
0001.1  1
1.1000  1
10.000  1
000.10  1
000010  1
000.10  1
0000.1  1
00.111  1
0.0001  1
0.0001  3
0.0010  1
0.0010  3
0.0100  1
0.0100  3
0.1000  1
0.1000  3
1.0000  1
1.0000  3
1.0001  1
1.0001  3
1.0010  1
1.0010  3
1.0100  1
1.0100  3
1.1000  1
1.1000  3
10.000  1
10.000  3
10.001  1
10.001  3
10.010  1
10.010  3
10.100  1
10.100  3
99.000  1
99.000  3
99.001  1
99.001  3
99.010  1
99.010  3
99.100  1
99.100  3
99.998  1
99.998  3
10.100  1
10.100  3
99.000  1
99.000  3
99.001  1
99.998  1

输出:

970328.403297001
99.01
970593.059701
99.1
973242.271
548815620517731830194541.899025343415715973535967221869852721
.00000005148554641076956121994511276767154838481760200726351203835429763013462401
43992025569.928573701266488041146654993318703707511666295476720493953024
29448126.764121021618164430206909037173276672
90429072743629540498.107596019456651774561044010001
1.126825030131969720661201
.00001
.12345
1.1
1.1
10
.1
0
0
1
10
.1
.1
.111
.0001
.000000000001
.001
.000000001
.01
.000001
.1
.001
1
1
1.0001
1.000300030001
1.001
1.003003001
1.01
1.030301
1.1
1.331
10
1000
10.001
1000.300030001
10.01
1003.003001
10.1
1030.301
99
970299
99.001
970328.403297001
99.01
970593.059701
99.1
973242.271
99.998
999940.001199992
548815620517731830194541.899025343415715973535967221869852721
.00000005148554641076956121994511276767154838481760200726351203835429763013462401
43992025569.928573701266488041146654993318703707511666295476720493953024
29448126.764121021618164430206909037173276672
90429072743629540498.107596019456651774561044010001
1.126825030131969720661201
.00001
.12345
1.1
1.1
10
.1
10
.1
.1
.111
.0001
.000000000001
.001
.000000001
.01
.000001
.1
.001
1
1
1.0001
1.000300030001
1.001
1.003003001
1.01
1.030301
1.1
1.331
10
1000
10.001
1000.300030001
10.01
1003.003001
10.1
1030.301
99
970299
99.001
970328.403297001
99.01
970593.059701
99.1
973242.271
99.998
999940.001199992
10.1
1030.301
99
970299
99.001
99.998

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值