题目一:满足 abcd = (ab + cd)²
的四位数
目标:找到所有四位数 abcd
(即 1000 ≤ abcd ≤ 9999
),满足其值等于前两位 ab
和后两位 cd
之和的平方。
数学性质:
-
四位数
abcd
可拆分为ab = 100a + 10b
和cd = 10c + d
,因此abcd = 100*ab + cd
。 -
等式要求:
100*ab + cd = (ab + cd)^2
。
算法核心:
-
数学范围缩小:
-
ab
和cd
均为两位数(10 ≤ ab, cd ≤ 99
)。 -
(ab + cd)
的平方为四位数,因此ab + cd ∈ [32, 99]
(因32² = 1024
,99² = 9801
)。
-
-
遍历与验证:
-
遍历
s = ab + cd
(32 ≤ s ≤ 99
),计算s²
。 -
将
s²
拆分为前两位ab
和后两位cd
,验证ab + cd = s
。
-
时间复杂度:
-
遍历次数固定为
99 - 32 + 1 = 68
次,时间复杂度为 O(1)。
优化点:
-
直接遍历
s
而非四位数,减少循环次数。 -
数学推导避免无效计算。
特别注意:
-
四位数的最小值 题目要求四位数
abcd
满足 1000 ≤ abcd ≤ 9999。 由于abcd = (ab + cd)²
,需满足: s=ab+cd⇒s2≥1000解得最小的整数s
为 32,因为: 312=961(小于 1000),322=1024(满足条件).312=961(小于 1000),322=1024(满足条件). -
四位数的最大值 最大的四位数是 9999,对应的最大
s
为:
-
(因为
).
#include <stdio.h>
/**
* 寻找满足条件的四位数:abcd = (ab + cd)^2
* 方法:遍历可能的和s(32到99),验证s²是否可拆分为ab和cd,使得ab + cd = s
*/
int main() {
// 遍历所有可能的s值(32到99)。s表示ab+cd的和,其平方为四位数,故s∈[32,99]
for (int s = 0; s <= 99; ++s) {
// 计算s的平方,即候选四位数(范围:32²=1024 至 99²=9801)
int s_squared = s * s;
// 分子:s² - s,用于后续计算ab。由等式100ab + cd = s²推导而来(cd = s - ab)
int numerator = s_squared - s;
// 关键数学验证:检查(s² - s)是否能被99整除
// 推导:100ab + (s - ab) = s² → 99ab = s² - s → ab = (s² - s)/99
if (numerator % 99 != 0) {
continue; // 不满足整除条件,跳过当前s
}
// 计算前两位数ab。根据公式 ab = (s² - s)/99
int ab = numerator / 99;
// 验证ab是否为有效的两位数(10 ≤ ab ≤ 99)
if (ab < 10 || ab > 99) {
continue; // ab越界,跳过
}
// 计算后两位cd。根据和的定义:cd = s - ab
int cd = s - ab;
// 验证cd是否为有效的两位数值(0 ≤ cd ≤ 99)
// 注意:cd作为整数允许0(如四位数2025的后两位"25"对应cd=25;9801的后两位"01"对应cd=1)
if (cd < 0 || cd > 99) {
continue; // cd越界,跳过
}
// 通过所有检查,输出符合条件的四位数
printf("%d\n", s_squared);
}
return 0;
}
另外一种方法: 全指针与while
#include <stdio.h>
/**
* 寻找满足 abcd = (ab + cd)² 的四位数
* 方法:遍历可能的和s(32到99),验证s²是否可拆分为ab和cd,使得ab + cd = s
*/
int main() {
int s = 32; // 初始化循环变量s为范围下限(因32²=1024是第一个四位数)
int *p_s = &s; // 定义指针p_s指向s,通过指针操作实现循环变量控制(演示指针用法)
while (*p_s <= 99) { // 遍历s的范围[32, 99],覆盖所有可能的四位数平方根
// 计算当前s的平方值,即候选四位数(如s=45时,s_squared=2025)
const int s_squared = *p_s * *p_s;
// 计算分子:s² - s。用于推导ab的值(公式:ab = (s² - s)/99)
const int numerator = s_squared - *p_s;
// 关键数学检查:分子必须能被99整除,否则ab非整数
if (numerator % 99 == 0) {
// 计算前两位数ab(公式:ab = (s² - s)/99)
int ab = numerator / 99;
// 计算后两位数cd(公式:cd = s - ab)
int cd = *p_s - ab;
// 验证ab和cd的有效性
// 注意:此处cd的范围检查存在潜在问题(见下方注释)
if (ab >= 0 && ab <= 99 // ab必须为有效两位数(10~99)
&& cd >= 0 && cd <= 99) { // cd检查范围错误!正确应为0~99
// 输出符合条件的四位数(如s=45时输出2025)
printf("%d\n", s_squared);
}
}
(*p_s)++; // 通过指针递增s的值(等价于s++,此处演示指针操作)
}
return 0;
}
输出结果:
题目二:二进制对称数
目标:找到自然数 n
,满足其 十进制形式 和 二进制形式 均为回文数(对称数)。
示例:
-
n = 5
,十进制5
是回文,二进制101
也是回文。
算法核心:
-
回文数判断:
-
十进制回文:将数字转为字符串,判断首尾对称。
-
二进制回文:将数字转为二进制字符串,去除前导零后判断对称性。
-
-
遍历与验证:
-
遍历自然数
n
,依次检查其十进制和二进制是否均为回文。
-
时间复杂度:
-
遍历次数取决于最大待检查值,若目标为
n ≤ N
,时间复杂度为 O(N * k),其中k
为数字位数。
优化点:
-
生成十进制回文数,再验证其二进制是否为回文(减少遍历次数)。
-
避免重复转换进制,缓存二进制字符串
|
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#define MAX_BINARY_LEN 32 // 32位整数的最大二进制长度(含终止符)
/**
* @brief 检查十进制数是否为回文
* @param n 待检查的自然数(需为非负整数)
* @return true-是回文,false-不是回文
* @note 通过反转数字进行验证,注意n为负数时直接返回false
*/
bool is_decimal_palindrome(int n) {
if (n < 0) return false; // 自然数不处理负数
int original = n; // 保存原始数值
int reversed = 0; // 存储反转后的数值
// 反转数字:逐位取模构建反转数
while (n > 0) {
reversed = reversed * 10 + n % 10; // 将低位数字逐步提升至高位置
n /= 10; // 移除已处理的最低位
}
return original == reversed; // 比较原始值与反转值
}
/**
* @brief 将十进制数转换为二进制字符串
* @param n 十进制数(需为非负整数)
* @param binary_str 存储二进制结果的字符数组(需预分配至少MAX_BINARY_LEN空间)
* @note 转换结果不包含前导零(除0本身),存储顺序为高位在前
*/
void decimal_to_binary(int n, char *binary_str) {
if (n == 0) { // 处理特殊情况:输入为0
strcpy(binary_str, "0"); // 直接赋值"0"
return;
}
int idx = 0; // 二进制字符串的当前写入位置
// 从低位到高位逐位计算二进制(此时得到的是逆序二进制)
while (n > 0) {
binary_str[idx++] = (n % 2) + '0'; // 将二进制位转换为ASCII字符
n /= 2; // 移除已处理的最低位
}
binary_str[idx] = '\0'; // 添加字符串终止符
// 反转字符串:将逆序转换为正序(高位在前)
for (int i = 0; i < idx / 2; i++) {
// 交换对称位置的字符
char temp = binary_str[i];
binary_str[i] = binary_str[idx - i - 1];
binary_str[idx - i - 1] = temp;
}
}
/**
* @brief 检查二进制字符串是否为回文
* @param binary_str 二进制字符串(需以'\0'结尾)
* @return true-是回文,false-不是回文
* @note 空字符串或单字符字符串视为回文
*/
bool is_binary_palindrome(const char *binary_str) {
int len = strlen(binary_str); // 获取字符串长度
// 遍历前半部分字符,与对称位置比较
for (int i = 0; i < len / 2; i++) {
if (binary_str[i] != binary_str[len - i - 1]) {
return false; // 发现不对称立即返回false
}
}
return true; // 全部对称则返回true
}
/*------------------------------------------
主程序逻辑
------------------------------------------*/
int main() {
//-- 用户输入处理 --
int max_num; // 存储用户输入的最大范围值
printf("请输入最大自然数范围(例如 1000): ");
scanf("%d", &max_num); // 读取用户输入(注意:未做输入验证)
//-- 核心计算逻辑 --
printf("满足条件的回文数:\n");
// 遍历0到max_num的每个数
for (int n = 0; n <= max_num; n++) {
// 先检查十进制回文性
if (is_decimal_palindrome(n)) {
char binary_str[MAX_BINARY_LEN]; // 存储二进制字符串
// 转换为二进制并检查二进制回文性
decimal_to_binary(n, binary_str);
if (is_binary_palindrome(binary_str)) {
// 输出符合条件的数值及二进制表示
printf("%d (二进制: %s)\n", n, binary_str);
}
}
}
return 0; // 正常退出程序
}
总结
-
问题性质差异:
-
四位数问题 是纯数学等式验证,依赖数值分解和代数运算。
-
二进制对称数 需结合不同进制的字符串操作和对称性判断。
-
-
算法策略差异:
-
四位数问题 通过数学约束将遍历范围从
9000
次优化到68
次,效率极高。 -
二进制对称数 需双重验证,性能依赖回文生成和进制转换的效率。
-
-
工程实践启示:
-
数学优化优先:对于数值问题,优先通过数学分析缩小搜索范围。
-
避免重复计算:缓存中间结果(如二进制字符串)以提高性能。
-
-
扩展性:
-
四位数问题 的解法仅适用于特定数学形式,难以推广。
-
二进制对称数 的算法可扩展至其他进制(如八进制、十六进制对称数)。
-