1. 题目描述
尽管是一个CS专业的学生,小B的数学基础很好并对数值计算有着特别的兴趣,喜欢用计算机程序来解决数学问题,现在,她正在玩一个数值变换的游戏。她发现计算机中经常用不同的进制表示一个数,如十进制数123表达为16进制时只包含两位数7、11(B),用八进制表示为三位数1、7、3,按不同进制表达时,各个位数的和也不同,如上述例子中十六进制和八进制中各位数的和分别是18和11,。 小B感兴趣的是,一个数A如果按2到A-1进制表达时,各个位数之和的均值是多少?她希望你能帮她解决这个问题? 所有的计算均基于十进制进行,结果也用十进制表示为不可约简的分数形式。
2. 题目分析
题目很长,这大概就是笔试题的特点,墨迹一堆,就看你能不能理解。大概的意思就是给你一个整数N,让你将N分别转化为2进制、3进制……N-1进制,然后把每一位的数字相加,对这几种进制求均值。假设T(N,X)为将整数N转化为X进制后的各位数字和,则Answer = [T(N,X=2)+T(N,X=3)+……+T(N,X=N-1)] / (N-1-1)。
3. ac解法——cpp
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
// 求整数x转换成n进制后各位数字的和
int everyByteSum(int x, int n, int &everysum) {
int yushu = x % n;
everysum += yushu;
if (x/n >= n) {
everyByteSum(x/n, n, everysum);
}
else {
everysum += x/n;
}
return everysum;
}
int main() {
int X;
cin >> X;
if (X < 1 || X > 5000) // 判定非法输入
return -1;
int everysum = 0;
int allsum = 0;
for(int i = 2; i < X; i++) { // 计算每一种进制的总和
everysum = 0;
allsum += everyByteSum(X, i, everysum);
}
string ans; // 答案要求输出分数形式,所以使用字符串
int len = X - 2;
// 进行分式化简
if (allsum % len == 0) {
allsum = allsum / len;
len = 1;
}
for(int i = 2; i <= len; i++) {
while(allsum%i == 0 && len%i == 0 && allsum/i!=0 && len/i!=0){
allsum = allsum / i;
len = len / i;
}
}
// 将结果转化为分式后输出
ans = to_string(allsum);
ans += "/";
ans += to_string(len);
cout << ans << endl;
return 0;
}
4. 总结
在这里分式化简的部分不够有普遍适用性。理论上应该先求取allsum和len的最大公约数之后,分别除掉最大公约数即可。在规定时间内时间有点紧张,一时掀不起来求最大公约数的算法,所以采用了上面化简的不优美的方法。所以在这里贴一下求最大公约数的算法,希望下次需要的时候能够信手拈来。
最大公约数的求取:
方法一:辗转相除法
int MaxCommonDivisor(int a, int b) {
if (a % b == 0)
return b;
else
return MaxCommonDivisor(b, a%b);
}
方法二:辗转相减法
int MaxCommonDivisor(int a, int b) {
if (a == b)
return a;
if (a > b)
return MaxCommonDivisor(a-b, b);
else
return MaxCommonDivisor(a, b-a);
}
方法三:暴力求解(如题目解答中化解部分)