Time Limit: 1000MS | Memory Limit: 30000K | |
Total Submissions: 14584 | Accepted: 7412 |
Description
This problem involves the efficient computation of integer roots of numbers.
Given an integer n>=1 and an integer p>= 1 you have to write a program that determines the n th positive root of p. In this problem, given such integers n and p, p will always be of the form k to the n th. power, for an integer k (this integer is what your program must find).
Input
Output
Sample Input
2 16 3 27 7 4357186184021382204544
Sample Output
4 3 1234
如果你只是想知道为什么double型开n次方法通过的原因,请跳过此部分。
题目大意:
求一个整数k,使得k满足kn=p。
思路:
由于p的值很大,超出了基本数据类型所表示的范围,所以采用大数乘法来计算kn,当kn=p时,得出结果(不知道能不能大数开n次方…)。那k值应该如何取呢?根据n和p的关系是可以确定出k的位数的,例如:n=7,p=4357186184021382204544,p的位数为22,用22/7的结果向上取整,得到4,即为k的位数,也就是说k的取值范围是1000~9999。在这个范围内进行二分查找,就可以找到满足条件的k值。(这狗屎题目中说“there exists an integer k, 1<=k<=109 , such that kn = p.”,其实…逗你玩!)
#include <stdio.h>
#include <string.h>
#include <math.h>
#define LENGTH 110
#define LAST LENGTH-2
#define GREATER 1
#define EQUAL 0
#define LESS -1
//大整数相乘
char* IntegerMultiplication(const char *a, const char *b, char *product)
{
int i, j, k = LAST, first, value, temp[LENGTH];
memset(temp, 0, sizeof(temp));
for (i = strlen(a)-1; i >= 0; i--)
{
first = k--;
value = a[i] - '0';
for (j = strlen(b)-1; j >= 0; j--)
{
temp[first--] += value * (b[j] - '0');
}
}
for (i = LAST; i >= first; i--)
{
product[i] = temp[i] % 10 + '0';
temp[i-1] += temp[i] / 10;
}
while (product[first] == '0' && first < LAST)
{
first++;
}
return &product[first];
}
//比较两个字符串所表示数值的大小
int Compare(char *numA, char *numB)
{
//去除前导'0'
for (; *numA == '0'; numA++);
for (; *numB == '0'; numB++);
int lenNumA = strlen(numA);
int lenNumB = strlen(numB);
if (lenNumA == lenNumB)
{
return strcmp(numA, numB);
}
if (lenNumA > lenNumB)
{
return GREATER;
}
return LESS;
}
//求base^exp,结果存放在res中,pRes指向结果的首位数字的位置
char* Power(char *base, int exp, char *res)
{
res[LAST] = '1';
char *pRes = &res[LAST], *temp = base;
while (exp != 0)
{
if (exp % 2 == 1)
{
pRes = IntegerMultiplication(base, pRes, res);
}
exp /= 2;
if (exp != 0)
{
base = IntegerMultiplication(base, base, temp);
}
}
return pRes;
}
int main(void)
{
char p[LENGTH], res[LENGTH], cMid[LENGTH];
unsigned int n, lenP, lenRoot, i, min, max, mid;
while (scanf("%d %s", &n, &p) != EOF)
{
//根据n和p的倍数关系,得到k的范围的min值和max值
lenP = strlen(p);
lenRoot = (int)ceil((double)lenP / n);
for (i = 1, min = 1; i < lenRoot; i++)
{
min *= 10;
}
for (i = 1, max = 9; i < lenRoot; i++)
{
max *= 10;
max += 9;
}
//二分法寻找k值
bool finish = false;
while (!finish)
{
mid = (min + max) / 2;
if (min >= max)
{
break;
}
sprintf(cMid, "%d", mid);
memset(res, 0, sizeof(res));
switch (Compare(Power(cMid, n, res), p))
{
case LESS: min = mid + 1; break;
case EQUAL: finish = true; break;
case GREATER: max = mid - 1; break;
default: break;
}
}
//由于题目所给数据会有不满足k^n=p的情况
//下面是为了得到一个最大的k,满足k^n<=p
sprintf(cMid, "%d", mid);
if (Compare(Power(cMid, n, res), p) == GREATER)
{
mid--;
}
printf("%d\n", mid);
}
return 0;
}
double型开n次方的方法通过的原因
下面这段程序也是可以通过此题的。
#include<stdio.h>
#include<math.h>
int main(void)
{
double n, p;
while(scanf("%lf%lf", &n, &p) != EOF)
{
printf("%.0lf\n", pow(p, 1/n));
}
return 0;
}
首先,题目中的数据强度并不弱,这一点确实如题目中所说:“For all such pairs 1<=n<= 200, 1<=p<10101,所以,double型是不能精确地表示出所给数据,但是却能表示出一个近似值。
当向double型变量中存入
4357186184021382204544
然后再输出,得到的是
4357186184021382000000
后六位的值变为了0,这一点和int型变量是有很大区别的。也就是说当存入double型变量的值超出了它的精度表示范围时,将低位的数据截断。(关于浮点数在计算机中的表示方法,百度吧…讲的蛮清楚的。)
在本题中,如果测试数据为:
7 4357186184021382204544
实际上所处理数据是:
7 4357186184021382000000
拿4357186184021382000000开7次方的结果自然就是1234。
为什么不是1233或者1235呢?
12337=4332529576639313702577
12347=4357186184021382204544
12357=4381962969567270546875
可以看出在double型所能表示的精度范围内,它们三个值已经不同了。
所以,此题中的测试数据也都是类似于上述情况,所以才能使用double型开n次方的方法。