【问题描述】
传送一个B(B≤16)进制的数值N时,最后加上一个一位(B进制的)校验码,使得N加上校验位后能被B-1整除。比如十进制的数值123(10),其校验码就是3,因为十进制数值1233(10)能被9整除。16进制的数78(16),其校验码为0,因为16进制的780(16)是15的倍数。超过十进制后,用字母a表示10,字母b表示11,字母c表示12,字母d表示13,字母e表示14,字母f表示15。
告诉你进制B,以及一个B进制的正整数N,要求你计算正整数N在B进制下的校验码。
【输入形式】
输入第一行正整数t (10 ≤ n ≤ 100),表示有多少组测试数据。
后面有t行,每行两个正整数B,N(2≤ B≤16),中间用一个空格隔开,B是10进制整数,N用B进制形式表示。测试数据保证没有非法的B进制数N(也即N中每一位都是在0到B-1之间,没有前导0)。
40%的测试数据N的位数L 1 ≤ L≤ 10;
30%的测试数据N的位数L 1 ≤ L≤ 10(2);
20%的测试数据N的位数L 1 ≤ L≤ 10(3);
10%的测试数据N的位数L 1 ≤ L≤ 10(4);
【输出形式】
对于每组测试数据,输出一位占一行:正整数N在B进制下的校验码。(如果校验码可以为B-1,也可以为0,输出0)。
【样例输入】
4
10 123
16 78
16 1234321
12 ab
【样例输出】
3
0
e
1
【提示】:B进制的数能被B-1整除,当且仅当各位数字和能被B-1整除。
解题思路
-
理解校验码的定义:校验码的设计是为了使得原数N加上这个校验位后的新数能被B-1整除。这告诉我们需要找到一个合适的校验码,加到N的末尾,使得整体数值模B-1的结果为0。
-
利用性质简化问题:根据提示,一个B进制数能被B-1整除的充要条件是它各位数字的和能被B-1整除。因此,我们不需要关心N具体的数值,只需要关注N的各位数字之和。
-
计算各位数字之和:将N的各位数字相加得到一个和sum。
-
计算校验码:找到一个数字X(0 ≤ X < B),使得(sum + X)能被B-1整除。X即为所求的校验码。checkSum = (B - 1) - (sum % (B - 1));
-
处理特殊情况:如果校验码可以为B-1,也可以为0,根据题目要求,我们输出0。
核心逻辑是计算输入的B进制数N的校验码,使得将校验码加到N的末尾后,得到的新数能够被B-1整除。这里使用了字符串处理的方式来逐个读取B进制数N的每一位,并将其转换为十进制的数值进行计算。最后,通过对各位数值之和进行特定的计算,找到符合条件的校验码,并根据题目要求进行输出。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int t = scanner.nextInt();
while(t-- > 0){
int B = scanner.nextInt(); // 读取进制B
String N = scanner.next(); // 读取B进制数N,以字符串形式读入,便于逐字符处理
int sum = 0; // 初始化变量sum用于存储N的各位数字之和
for (int i = 0; i < N.length(); i++) {
char c = N.charAt(i);
int value; // 用于存储当前字符代表的数值
// 判断当前字符是数字还是字母,并转换为对应的数值
if(c >= '0' && c <= '9') value = c - '0'; // '0'到'9'之间的字符转换为0到9的数值
else value = 10 + (c - 'a'); // 'a'到'f'之间的字符转换为10到15的数值
sum += value;
}
// 计算校验码:(B - 1) - checkSum = (sum % (B - 1)),这里的目的是找到一个数X使得(S + X)能被B-1整除
int checkSum = (B - 1) - (sum % (B - 1));
// 如果计算出的校验码等于B-1,根据题目要求,将其替换为0
if(checkSum == B-1) checkSum = 0;
// 根据校验码的值输出对应的字符
// 如果校验码大于9,需要转换为'a'到'f'之间的字母
if(checkSum > 9) System.out.println((char)(checkSum - 10 + 'a'));
else System.out.println(checkSum);
}
scanner.close();
}
}