B3940 [GESP样题 四级] 填幻方
题目
在一个N×N 的正方形网格中,每个格子分别填上从 1 到 N×N 的正整数,使得正方形中任一行、任一列及对角线的几个数之和都相等,则这种正方形图案就称为“幻方”(输出样例中展示了一个3×3 的幻方)。我国古代称为“河图”、“洛书”,又叫“纵横图”。幻方看似神奇,但当 N 为奇数时有很方便的填法:
- 一开始正方形中没有填任何数字。首先,在第一行的正中央填上 1。
- 从上次填数字的位置向上移动一格,如果已经在第一行,则移到同一列的最后一行;再向右移动一格,如果已经在最右一列,则移动至同一行的第一列。如果移动后的位置没有填数字,则把上次填写的数字的下一个数字填到这个位置。
- 如果第 2 步填写失败,则从上次填数字的位置向下移动一格,如果已经在最下一行,则移到同一列的第一行。这个位置一定是空的(这可太神奇了!)。把上次填写的数字的下一个数字填到这个位置。
- 重复 2、3 步骤,直到所有格子都被填满,幻方就完成了!
快来编写一个程序,按上述规则,制作一个N×N 的幻方吧。
输入为一个正奇数 N,保证 3≤N≤21。
输出 N 行,每行 N个空格分隔的正整数,内容为N×N 的幻方。
运行代码
#include <iostream> #include <vector> using namespace std; void FN(int N) { vector<vector<int>> FF(N, vector<int>(N, 0)); int num = 1; int r = 0, c = N / 2; while (num <= N * N) { // 将数字填入当前位置 FF[r][c] = num++; // 计算下一个位置 int R= (r - 1 + N) % N; int C = (c + 1) % N; // 检查下一个位置是否已被占用 if (FF[R][C] != 0) { r = (r+ 1) % N; } else { r = R; c = C; } } // 打印幻方 for (int i = 0; i < N; ++i) { for (int j = 0; j < N; ++j) { cout << FF[i][j] << (j < N - 1 ? " " : "\n"); } } } int main() { int N; cin >> N; if (N % 2 == 0 || N < 3 || N > 21) { return 1; } FN(N); return 0; }
思路
- 初始化一个N x N的二维向量
vector<vector<int>> FF(N, vector<int>(N, 0));
,用来存储幻方数据,初始值全为0。设置计数器num = 1
,用于填充数字。 - 定义两个变量
r
和c
来追踪当前填充的位置,初始位置设在中心(r = 0, c = N / 2)
。 - 使用while循环,直到所有数字填充完毕(
num <= N * N
)。 - 在当前位置
(r, c)
放入数字num
,然后递增num
。 - 计算下一个位置的行
R
和列C
,使用取模运算保证位置在矩阵范围内。 - 如果下一个位置已占用,则向下一行移动;否则,更新当前位置为计算出的下一个位置。
B3850 [GESP202306 四级] 幸运数
题目
小明发明了一种 "幸运数"。一个正整数,其偶数位不变(个位为第 1 位,十位为第 2 位,以此类推),奇数位做如下变换:将数字乘以 7,如果不大于 9 则作为变换结果,否则把结果的各位数相加,如果结果不大于 9 则作为变换结果,否则(结果仍大于 9)继续把各位数相加,直到结果不大于 9,作为变换结果。变换结束后,把变换结果的各位数相加,如果得到的和是 8 的倍数,则称一开始的正整数为幸运数。
例如,16347:第 1位为 7,乘以 7 结果为 49,大于 9,各位数相加为13,仍大于9,继续各位数相加,最后结果为4;第3 位为3,变换结果为3;第 55 位为 1,变换结果为 7。最后变化结果为 76344,对于结果76344 其各位数之和为24,是 8的倍数。因此 16347 是幸运数。
输入第一行为正整数 N,表示有 N个待判断的正整数。约定 1≤N≤20。从第 2 行开始的 N 行,每行一个正整数,为待判断的正整数。约定这些正整数小于 10^12。
输出 N行,对应 N 个正整数是否为幸运数,如是则输出 'T',否则输出 'F'。
提示:不需要等到所有输入结束在依次输出,可以输入一个数就判断一个数并输出,再输入下一个数。
运行代码
#include <iostream> #include <string> #include <algorithm> using namespace std; int FN(long long num) { int sum = 0; while (num > 0) { sum += num % 10; num /= 10; } return sum; } // 函数:对奇数位进行变换 string FF(const string& numStr) { string result; for (size_t i = 0; i < numStr.size(); ++i) { if (i % 2 == 0) { // 偶数位直接添加 result += numStr[i]; } else { // 奇数位进行变换 int digit = numStr[i] - '0'; int t = digit * 7; while (t> 9) { t= FN(t); } result +=to_string(t); } } return result; } // 函数:判断是否为幸运数 bool Number(const string& numStr) { string t = FF(numStr); return FN(stoll(t)) % 8 == 0; } int main() { int N; cin >> N; cin.ignore(); // 忽略可能存在的换行符 while (N--) { string numStr; getline(cin, numStr); // 读取一行字符串作为数字 if (Number(numStr)) { cout << "T" << endl; } else { cout << "F" << endl; } } return 0; }
思路
- 计算一个数的各位数之和。
FN
:对输入的数字字符串的奇数位进行变换,并返回变换后的字符串。Number
:判断一个数字字符串是否为幸运数。
在main
函数中,我们读取要判断的正整数个数N
,然后对每个正整数进行判断并输出结果。注意我们使用getline
来读取每行的输入,以正确处理可能包含前导零的情况。此外,我们使用stoll
将变换后的字符串转换回long long
类型,以计算其各位数之和。