题解:洛谷 P1013 进制位
【题目来源】
洛谷:P1013 [NOIP 1998 提高组] 进制位 - 洛谷
【题目描述】
著名科学家卢斯为了检查学生对进位制的理解,他给出了如下的一张加法表,表中的字母代表数字。例如:
其含义为:
\(L+L=L\),\(L+K=K\),\(L+V=V\),\(L+E=E\)
\(K+L=K\),\(K+K=V\),\(K+V=E\),\(K+E=KL\)
\(\cdots\)
\(E+E=KV\)
根据这些规则可推导出:\(L=0\),\(K=1\),\(V=2\),\(E=3\)。
同时可以确定该表表示的是 \(4\) 进制加法。
【输入】
第一行一个整数 \(n\)(\(3\le n\le9\))表示行数。
以下 \(n\) 行,每行包括 \(n\) 个字符串,每个字符串间用空格隔开。
若记 \(s_{i,j}\) 表示第 \(i\) 行第 \(j\) 个字符串,数据保证 \(s_{1,1}=\texttt +\),\(s_{i,1}=s_{1,i}\),\(|s_{i,1}|=1\),\(s_{i,1}\ne s_{j,1}\) (\(i\ne j\))。
保证至多有一组解。
【输出】
第一行输出各个字母表示什么数,格式如:L=0 K=1 \(\cdots\) 按给出的字母顺序排序。不同字母必须代表不同数字。
第二行输出加法运算是几进制的。
若不可能组成加法表,则应输出 ERROR!。
【输入样例】
5
+ L K V E
L L K V E
K K V E KL
V V E KL KK
E E KL KK KV
【输出样例】
L=0 K=1 V=2 E=3
4
【核心思想】
-
问题分析:给定一个 \(n \times n\) 的加法表,第一行和第一列为 \(n-1\) 个不同字母(代表数字),表内是这些字母相加的结果(可能是一位或两位字母)。需要确定每个字母代表的数字(0 到 \(n-2\))以及该加法表的进制(\(n-1\) 进制)。若不存在合法映射,输出
ERROR!。本质是一个进制加法表验证与映射问题,利用加法表的性质(如某个字母的进位规律)可以推导出映射关系。 -
算法选择:
- 推导映射:在 \(k\) 进制加法中,一个字母若在加法结果中多次出现在十位(即作为两位数的第一位),则它代表的数字通常为 1。通过观察加法表,可以找到代表数字 0 和 1 的字母,然后利用加法规则推出其他字母的映射。但代码中使用了一种基于“关键字母”的推导方法,复杂度 \(O(n^2)\)。
- 验证:得到映射后,遍历表内所有加法项,检查是否满足
(字母值 + 字母值) == 结果值(按 \(k\) 进制计算)。
-
关键步骤(基于代码逻辑):
- 读入:读取 \(n\) 和 \(n \times n\) 个字符串。
- 推导映射:
- 进制 \(k = n-1\)。
- 找到第一个出现在两位数十位上的字母
c[1](即进位标志)。 - 找到对应位置的行列,确定数字 0 对应的字母
c[0]。 - 逐步推导出所有字母的数字映射。
- 验证:对每个加法项
s[i][j],计算find(s[i][1][0]) + find(s[1][j][0]),与解析s[i][j]得到的值(按 \(k\) 进制)比较。若全部相等,则映射合法。 - 输出:按第一行的顺序输出映射,再输出进制 \(k\)。
-
时间/空间复杂度:
- 时间复杂度:\(O(n^2)\)。
- 空间复杂度:\(O(n^2)\)。
-
进制加法表推导:
- 进制确定:\(n-1\) 个不同字母表示 \(0\) 到 \(n-2\),因此进制为 \(n-1\)。
- 进位规律:在加法中,若结果长度大于 1,则高位的字母通常代表 1(因为最大进位是 1)。
- 验证必要性:推导出的映射可能不唯一或错误,必须通过遍历表验证。
- 适用场景:这类“进制加法表”问题,是模拟与数学推导的结合,常见于 NOIP 提高组。
【算法标签】
普及+ #模拟
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int n; // 矩阵的大小 n x n
int k; // 进制数 k = n - 1
int row, col; // 用于记录关键行和列的位置
string s[15][15]; // 存储输入的矩阵
char c[15]; // 存储数字到字符的映射关系
/**
* 查找字符在映射数组中的索引
* @param x 要查找的字符
* @return 字符对应的索引值
*/
int find(char x)
{
int res = 0;
for (int i = 0; i < k; i++)
{
if (c[i] == x)
{
res = i;
break;
}
}
return res;
}
/**
* 检查矩阵是否满足加法运算规则
* @return 如果矩阵满足规则返回true,否则false
*/
bool check()
{
// 检查矩阵中每个位置是否满足加法规则
for (int i = 2; i <= n; i++)
{
for (int j = 2; j <= n; j++)
{
// 计算期望值:行首 + 列首
int x = find(s[i][1][0]) + find(s[1][j][0]);
int y = 0;
// 解析矩阵中的实际值
if (s[i][j].size() == 1)
{
// 一位数的情况
y = find(s[i][j][0]);
}
else
{
// 两位数的情况:高位*k + 低位
y = find(s[i][j][0]) * k + find(s[i][j][1]);
}
// 检查期望值是否等于实际值
if (x != y)
{
return false;
}
}
}
return true;
}
int main()
{
// 输入矩阵大小
cin >> n;
k = n - 1; // 进制数为n-1
// 输入矩阵数据
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
cin >> s[i][j];
// 如果单元格是两位数,记录第一个字符作为关键字符
if (s[i][j].size() == 2)
{
c[1] = s[i][j][0];
}
}
}
// 在第一行中找到包含关键字符的列
for (int i = 2; i <= n; i++)
{
if (s[1][i][0] == c[1])
{
col = i; // 记录关键列
break;
}
}
// 在关键列中找到包含关键字符的行
for (int i = 2; i <= n; i++)
{
if (s[i][col].size() == 1 && s[i][col][0] == c[1])
{
row = i; // 记录关键行
c[0] = s[row][i][0]; // 记录0对应的字符
break;
}
}
// 构建完整的字符映射表
for (int i = 1; i <= k - 2; i++)
{
// 找到当前字符对应的行
for (int j = 2; j <= n; j++)
{
if (c[i] == s[j][1][0])
{
row = j;
break;
}
}
// 记录下一个字符
c[i + 1] = s[row][col][0];
}
// 验证矩阵的正确性
if (check() == false)
{
cout << "ERROR!" << endl;
return 0;
}
// 输出字符到数字的映射关系
for (int i = 2; i <= n; i++)
{
printf("%c=%d ", s[1][i][0], find(s[1][i][0]));
}
cout << endl << k << endl;
return 0;
}
【运行结果】
5
+ L K V E
L L K V E
K K V E KL
V V E KL KK
E E KL KK KV
L=0 K=1 V=2 E=3
4
浙公网安备 33010602011771号