最近在准备东华大学计算机复试的机试环节,刷了不少OJ题目。今天想和大家分享三道比较有代表性的基础题(编号16-18),这几道题看似简单,但想要高效解决还是需要一些技巧的。我会详细分析每道题的解题思路,并给出优化建议。
给定一个正整数N,计算N的所有因子之和与N的比值,结果保留两位小数。例如N=6时,因子为1,2,3,6,和为12,比值为2.00。
这道题的核心在于如何高效找出一个数的所有因子。最直观的方法是遍历1到N,检查每个数是否能整除N。但这种方法时间复杂度为O(N),当N较大时效率不高。
优化思路是只需遍历到√N:
原代码采用了遍历到N/2的方法,虽然不是最优,但对于OJ题目通常够用。这里给出优化后的版本:
c复制#include <stdio.h>
#include <math.h>
int main() {
int N;
while (scanf("%d", &N) != EOF) {
int sum = 1 + N; // 1和N本身一定是因子
for (int i = 2; i <= sqrt(N); i++) {
if (N % i == 0) {
sum += i;
if (i != N/i) sum += N/i; // 避免重复加完全平方数
}
}
printf("%.2f\n", (float)sum / N);
}
return 0;
}
注意:在OJ系统中,有时简单的实现反而比过度优化的代码更不容易出错,特别是在时间限制不严格的情况下。
给定倍数T,寻找最小的正整数N满足:
例如T=3时,N=179487,因为:
179487 → 717948
717948 = 3 × 179487
这道题需要将数字变换转化为数学表达式。设:
根据题意有:7×10^k + a = T×(10a +7)
解这个方程可以得到:a = (7×10^k - 7T)/(10T -1)
因此算法步骤:
原代码采用暴力枚举,效率较低。改进版可以利用数学关系缩小搜索范围:
c复制#include <stdio.h>
#include <math.h>
int main() {
int T;
while (scanf("%d", &T) != EOF) {
int found = 0;
for (int k = 1; k <= 6; k++) { // 因为N<=1e6,所以k最多6位
long numerator = 7 * (pow(10, k) - T);
long denominator = 10 * T - 1;
if (denominator <= 0) continue;
if (numerator % denominator == 0) {
long a = numerator / denominator;
if (a >= 0) {
long N = 10 * a + 7;
if (N <= 1000000) {
printf("%ld\n", N);
found = 1;
break;
}
}
}
}
if (!found) printf("No\n");
}
return 0;
}
给定起始数字s和三角形行数n,构建数字三角形:
例如s=1,n=5:
1
2 3
4 5 6
7 8 9 1
2 3 4 5 6
这道题主要考察循环控制和格式化输出。关键点:
原代码已经比较清晰,这里稍作优化:
c复制#include <stdio.h>
int main() {
int s, n;
while (scanf("%d %d", &s, &n) != EOF) {
int current = s - 1; // 调整为0-based便于取模
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= i; j++) {
current = (current % 9) + 1; // 保证在1-9之间循环
printf("%d", current);
if (j < i) printf(" ");
}
printf("\n");
}
printf("\n");
}
return 0;
}
这三道题虽然被标记为"基础",但涵盖了算法竞赛中的几个重要方面:
在实际刷题过程中,我有几点心得:
对于准备机试的同学,建议: