本来是写给一个女同学看的,没想到会有这么多人看,虽然我已经毕业了,但是我还是维护一下吧。下面放一下我4年来的战果(
有点水请见谅)
大一到大三从未系统学过算法类课程,零散的看了一下算法书籍,浅学了一些树,图,和其他杂七杂八的算法。直到大四开始看《算法设计与分析基础第3版》(这里强烈推荐,不可多得的好书,能帮你梳理你零散的算法成体系)。我花了半个月看完,并且在看的过程中刷HDOJ的集训题目(现在好像非本校不让进了 )。累加花费一个月左右时间,感觉解题思路一下子清晰明亮。
然后半个月后省赛(轻松拿了省一第4顺位 ),如果顺着这个势头下去估计是拿国一无压力的吧(实际上离国一差了就10个名次,就是自己太菜了 几乎是裸考,因为要毕业了准备论文,准备找工作,太忙了 。
先在这里给大家推荐几本算法书吧:
《算法设计与分析基础第3版》- 帮你梳理你的算法知识体系,授人以渔
《算法4》- 开拓你的算法思想,不可多得的算法书,难度坡度较大(本人通读过一遍,里面有一些高阶的算法非常实用)
《数据结构基础 - 王道》 - 虽然是考研用的书籍和视频,但是能帮你打下扎实的基础(我大四看过一遍,感觉基础变牢固很多)
赠送一句心得:脚踏实地,一个算法一个算法实践,刷题,总结,对比,看似是最慢的,其实是最快的。
开始吧:从最基础 的
蓝桥杯最新的参赛选手训练系统,大家可以上去刷题来进行实践。有很多学校自己组织有培训,但是蓝桥杯的比赛环境或许会有不同,在比赛前熟悉一下环境是不是能跑通自己的代码也是很重要的!!!
比赛开始前:
一般比赛的文件会加密,然后比赛开始的时候老师会发一个密码给大家。文件开头有当前参数选手需要注意的,一定要看!!!这里会介绍你的参赛环境相关的注意事项。
类似比如:
Java: 你的类名必须是Main,并且不能有package语句。
C/C++:你的main函数返回值必须是0,也就是return 0;
下面虽然是C/C++语言描述的,但对于java来说其实同样实用(毕竟两个语言在很多方面很像,而且算法不考语法)。
代码模板 c/c++
比如输入啥的, 那好吧,就输入了。(简单放一点在这里)。
但是在输入之前, 先列举以下写代码的框架。(很low反正你们都知道qwq )
// 这是写代码的模板, 请你开始写代码第一件事就是将下面写出来
#include<stdio.h>
#include<bits/stdc++.h>// 万能头文件, 蓝桥杯是支持的。可以用。
// 这里添加变量
// 这里后面可以添加函数
int main(){
// 这里写代码。。。。。
return 0;// 注意要return 0;
}
我在评论区看到了如下评论:
A :会增加代码运行时间(编译时长),蓝桥杯代码大题有时长要求慎用
我的回答:
其实这个评论很严谨,确实是可能有一点点,我没有验证过,官方也没提到,如果你在意就背头文件好了(反正我背不住),我参赛情况来看,即使是最后一个大题该过还是得过,算法才是速度得80%左右,然后就是对于一些代码级别得细节优化占10%左右。当然还有一点运气成分(毕竟环境并不是完全稳定的)。
好了开始输入
对于输入来说 比如题目 :
第一行输入一个数 N ,接下来一行 ( 或者 N 行,每行一个数字 ) 。
接下来是数据范围(题目会给,一般在题目最后, 题目中或许会隐含有范围要注意)。
对于10%的数据0 <= N <= 100
对于30%的数据0 <= N <= 1000
对于100%的数据0 <= N <= 50000
在处理输入前一定要看数据范围!!!非常重要,重要!!!!!!
那么 请仔细看 注释注释 。
下面开始正式写代码。
#include<bits/stdc++.h>
int MAX = 50005;// 这里是50000 + 5 也就是100%的数据范围 + 5, 定义在这里
int N = 0; // 这样的数据定义为全局变量方便使用。
int num[MAX] = {};// 定义一个数组存放数据
// 用MAX做为数组长度
// = {}; 是初始化数组数据为0;
int main(){
// 对于输入一律用scanf, 不管是字符串还是啥。(~~当然还有快读,但是蓝桥杯用不到~~ )
scanf("%d", &N);// 输入N ~~记得取址符号~~
for(int i = 0; i < N; ++i){// 最简单的循环
scanf("%d", num + i);// 输入N行数据
}// 输入结束
return 0;// 注意要return 0;
}
进阶技巧:
数据范围隐含了当前题目需要用到算法的时间复杂度,比如
[0,10000]
内的可以使用时间复杂度为N^2
的算法,[0,100000]
使用N * logN
,再往上一般是[0 <= N <= 100000000
],只能用N
,再往上就是logN
了(蓝桥杯没有,我就没见过)。
题目时间限制表示你当前程序执行如果超过这个时间就会被判超时,如果一个数据点超时就是0分(蓝桥杯一般会有10个数据点,从低数据量到高数据量执行,数据量越高得分越多),这个时间范围不会影响到你决策算法时间复杂度,这个一般是用来平衡当前题目的常数值,也就是C * N的算法可以通过,这个C是一个常数,也就是如果你在代码级别上优化好了才可以通过。给你加了一个C值一般是因为题目复杂需要进行平衡,或者放宽了要求。
骗分技巧:(不知道蓝桥杯官网看到了会不会打我
如果你实在不会做,题目不是有输入样例和输出样例吗!我们可以在做不出来的情况下,或者时间不够的情况下将输入样例固定输出。
比如:
if 输入 == 3,输出10
if 输入 == 4,输出20
这里可以拿到样例数据点的分数,(不知道蓝桥杯改了没有如果没用就给自己一个心里安慰,好歹自己做了。
输出:
这里接着上面的题目写。
题目:
你需要输出这 N 个数的逆序对的个数。(就是输出一个数)。
接着上面的程序继续。
#include<bits/stdc++.h>
int MAX = 50005;
int N = 0;
int num[MAX] = {};
/
int answer = 0;// 根据题目要求新增一个变量, 用来记录答案,
// 如果答案是一串数字需要用数组。
/
int main(){
scanf("%d", &N);
for(int i = 0; i < N; ++i){
scanf("%d", num + i);
}// 输入结束
// 假设你已经计算完结果
// 那么这里可以开始输出
printf("%d\n", answer);// 注意格式(格式不对可能没分),后面一般要跟一个换行符, 就是输出最后一行后要换行。
return 0;
}
杂项
主函数return 0;
一定记得!
输入/出 long long数据需要使用的是
long long x = 0;
scanf("%I64d", &x);
printf("%I64d", x);
对于数组来说, 静态申请的一维数组最大长度大概为:
int num[500000000]; // int 可申请5*10的8次方左右
long long num[200000000]; // long long 可申请2*10的8次方左右
注:蓝桥杯最大栈空间为256MB,经过换算, 你最大可以开 1 * 10的7次方左右的数组空间。也就是1千万。如下:
数据的范围:
需要根据不同的取值范围,确定使用不同的数据类型。当超过20位的数据就要考虑其余的解法,或者自己写大数的运算了(这是我最不想遇到的)。
unsigned int 0~4294967295 // 9及以下位数都可装
int -2147483648~2147483647 // 9及以下位数都可装
unsigned long 0~4294967295 // 9及以下位数都可装
long -2147483648~2147483647 // 9及以下位数都可装
long long的最大值:9223372036854775807 // 18及以下位数都可装 19位也差不多
long long的最小值:-9223372036854775808 // 18及以下位数都可装 19位也差不多
unsigned long long的最大值:18446744073709551615 //20位
// 下面用的可能没有接触过, 但存在, 有上面的就够了, 下面和上面的long long 是一样的。
__int64的最大值:9223372036854775807
__int64的最小值:-9223372036854775808
unsigned __int64的最大值:18446744073709551615
下面是官网截图:
当然还有蓝桥杯的最大栈空间是256MB。下面为截图。
一些简单的优化问题 * 不涉及算法(可能大家都知道)
- 当需要多次使用一个表达式的值的时候, 可以存起来。 这样可以减少计算次数。
int n = 10;
int b = 30;
for(int i = 0; i < n; i++){
printf("%d", n * b * i);
}
还有就是,计算一个数组 / 或者字符串长度的时候, 最后一直存着,以免多次计算。
// 更改为
int n = 10;
int b = 30;
int t = n * b;
for(int i = 0; i < n; i++){
printf("%d", t * i);
}
- 多使用位运算
// 如:
int n = 30;
int i = n* 2;
int c = n / 16;
// 可以更改为
int i = n << 1; // 相信我会快。
int c = n >> 4;
如:
int i = 100;
while(i % 2 == 1){// 对于for循环同样使用。
i--;
}
// 改为
while(i & 1){ // 用位运算代替
--i;// 前自减/增 比 后自减/增快。
}
如:
int i = 0;
int x = i--;
// 改为
int x = i;
--i;// 这样结果一样, 但编译后,会少一条汇编指令。