题解 P7106 【双生独白】

· · 题解

P7106 双生独白

Analysis

众所周知,C/C++ 选手可以使用 scanf 直接读入 16 进制数字:scanf("%x", &a)

对于输出 16 进制数字,可以使用 printf("%x", a)printf("%X", a),其中 x 的大小写控制的是输出十六进制中字母字符的大小写。例如 printf("%x %X", 10, 10); 的结果为 a A

下一个问题是如何把读入割裂成三个数:可以先读入到字符数组里,然后使用 memcpy 把两个字符从数组中拷贝出来,再用 sscanf 从字符数组中读入数据,其使用方式为 scanf(A, B, ...),其中 A 表示目标数组,B 与后面的内容与 scanf 使用方法一致。例如,从字符数组 s 中读入一个整数的写法是 scanf(s, "%d", &a)memcpy 的使用方式为 memcpy(t, s, x),其中 t 是拷贝的目标数组,s 是被拷贝位置的第一个指针,x 是被拷贝的字节数。例如,从从 s 中拷贝两个字符到 t 的写法是 memcpy(t, s, 2 * sizeof (char))

字符数组的数组名本质上是一个指针,因此可以通过传入头指针的方式控制从哪一位开始读入/拷贝。例如 scanf(s + 1, "%d", &a) 表示从数组 s 中第一位(下标从 0 编号)起进行读入,memcpy(t, s + i, 2 * sizeof(char)) 表示从第 i 位起开始进行拷贝,拷贝两个字符。

最后一个问题是,如果输出某个数的十进制值不超过 15,那么输出结果会少一位,因此需要进行特判。好像有一些奇技淫巧可以避免这个问题,但是我并没有查到(。

于是可以方便的写出如下代码:

#include <cstdio>
#include <cstring>

char s[10];
char t[3];

int main() {
  scanf("%s", s);
  putchar('#');
  for (int i = 1; i < 6; i += 2) {
    int x;
    memcpy(t, s + i, 2 * sizeof(char));
    sscanf(t, "%x", &x);
    x = 255 - x;
    if (x < 16) putchar('0');
    printf("%X", x);
  }
  return 0;
}

upd:经过查阅资料,发现可以用 scanf("%2d", &a) 控制只读取两位数字,printf("%02d", a) 控制输出两位数字并补前导 0。因此可以写出更简短的代码如下:

#include <cstdio>

char s[10];

int main() {
  scanf("%s", s);
  putchar('#');
  for (int i = 1, x; i < 6; i += 2) {
    sscanf(s + i, "%2x", &x);
    printf("%02X", 255 - x);
  }
  return 0;
}