Time Limit: 1000MS | Memory Limit: 30000K | |
Total Submissions: 14584 | Accepted: 7412 |
Description
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次方的方法。