今天和一个参加CSP复赛的家长交流了些事情,所以有感写了这个。如果觉得不对,欢迎拍砖。
问题来由
山东某家长,小朋友刚参加了 CSP-J 组复赛,第二题出现 TLE ,整个山东第二题 100 分只有 5 人。家长说孩子思路是正确的,能否申述。我和家长巴拉巴拉了一大堆,这些不是核心。我告诉家长,OI考试核心是算法的复杂度,而不是思路。任何题目都有好多种解法。OI考试的核心是数学,是数学,是数学。重要的事情说三遍。我用一个具体题目来分析问题。
题目
原题
HDU编号6124,http://acm.hdu.edu.cn/showproblem.php?pid=6124。
描述
HazelFan 有两个正整数 a,b,他想计算 a mod b。 但现在他忘了 b 的值,只记得 a 的值,请告诉他不同的可能的求余结果的数量。
输入
第一行包含一个正整数 T(1 ≤ T ≤ 5),表示测试样例数。
对于每个测试样例: 一行,包含正整数 a(1 ≤ a ≤ 10^9)。
输出
对于每个测试用例: 单行包含一个非负整数,表示答案。
样例输入
2
1
3
样例输出
2
3
题目分析
就是求 a % b 值的可能性。
数学知识
数学的取模运算,而且是整数的取模运算。按照数学的定义,a % b 的余数 r ,r = a - k * b,其中 k = b / a 的商。
数据范围
正整数 T(1 ≤ T ≤ 5)。哪么原题你爱怎么写都能 100% 通过。
哪么我们用初学者最容易的方法,枚举遍历呗。
AC代码
#include <iostream>
using namespace std;
int main() {
int t;
cin>>t;
while(t--) {
int a,b=1,result = 1;
cin>>a;
for(;b<=a;b+=2) {
result+=1;
}
cout<<result<<endl;
}
return 0;
}
复杂度分析
老铁,没问题,标准O(n^2)。
等一下,问题解决了?我们将题目做个修改,
升级版题目
题面不做任何修改,只是吧 T 的范围扩大到10^6,也就是 1e6 的数据集。欢迎参考以下的题目链接,http://47.110.135.197/problem.php?id=5121。
输入
第一行包含一个正整数 T(1 ≤ T ≤ 10^6),表示测试样例数。
对于每个测试样例: 一行,包含正整数 a(1 ≤ a ≤ 10^9)。
题目分析
题目没有任何的变化,就是数据集放大,也就是 T 从 5 变成了 1e6 大小。
哪么意味着O(n^2)复杂度的解法,必然出现 TLE ,也就是说只能拿到部分分数。让OJ重新判决题目。果然结论如下图。
也就是意味必须对原有算法进行升级,才能拿到 AC 。
数学分析
本题的核心是数学。下面我们简单分类讨论一下,a mod b 情况:
1、如果 a ≤ b,则 a % b 有 (n + 1) / 2 种可能.;
2、如果 a ≥ b,则有 1 种可能.。
所以,总共有 (n + 1 ) / 2 + 1 种可能.。
哪么这样,利用数学分析,我们就将算法从 O(n^2) 变成 O(n)。其实 AC 代码不是核心,但是还是贴一下吧。
AC代码
#include <cstdio>
int main() {
//freopen("1.in", "r", stdin);
//freopen("1.out", "w", stdout);
int t;
scanf("%d", &t);
int a;
while (t--) {
scanf("%d", &a);
printf("%d\n", (a+1)/2+1);
}
//fclose(stdin);
//fclose(stdout);
return 0;
}
结论
OI考试核心是算法的复杂度,而不是思路。任何题目都有好多种解法。OI考试的核心是数学,是数学,是数学。重要的事情说三遍。
P.S.
道歉,工科男,文笔实在不咋地。大家不要介意。