一、暴力求解
1. 枚举法
2. 模拟法
2.1 图形排版
2.2 日期问题
二、查找与排序
1.排序
2.查找
三、字符串
1 普通类型
2 字符串匹配(KMP算法)
N、通用问题
总结中题型分类、大体框架来自王道机试指南,同时也参考了算法笔记的内容。我自己的编程基础比较薄弱,在学习刷题的过程中,对算法笔记的直观感受是难度相对机试指南要大一些,但是 PAT 题很强调考虑问题的全面性,尤其是对于边界等特殊情况的考虑,题目都很经典。机试指南习题难度较小,如果从头开始的话,个人建议是 一定的C/C++基础 --》 机试指南 --》算法笔记。
本文写作的侧重点不在于精,主要是想全面的对考研机试的考试范围、考察形式等进行说明,仍处在不断的修正改进之中,若有不同意见,欢迎留言交流。
最后祝看到此文的战友们都能如愿上岸,我们一起在人生的更高处相见吧!
适用于问题规模较小,可以一个答案一个答案试统计所有的结果。一般问题的规模不能超过 10 的 6 次方。
按照题目的描述,直接编程输出即可。
按照能否直接在输出图形中找到规律,采用两种方法。第一种是直接输出,适用于较为简单的题目,第二种方法是构造矩阵,改变矩阵直接输出,好处是可以从各个方向进行访问修改,直接输出的方法仅适用于从上到下,从左到右的情况。
1.3.1 直接输出
观察输出规律,直接输出。
浙江大学真题
1.3.2 构造矩阵
构造并修改矩阵最后输出。
PAT B 1050
1.4.1 给出日期,求是此年的第几天,此类问题直接累加即可,不要要注意平年和闰年的二月的区别。
判断平闰年并不是采用是否能被 4 整除的方法,要用以下方法:
bool isLeap(int year){
return ((year % 4 == 0) && (year % 100 != 0)) || year % 400 == 0;
}
1.4.2 给出第几天,求具体日期。累加即可。
1.4.3 求两个日期之间的隔的天数。
codeup 1928
此类题目务必掌握 c++ 中 sort 函数 或 c 中 qsort 函数详细的使用用法,建议使用 c++ 中 algorithm 库中的 sort 函数,使用方法较为简单,练习题牛客、PAT、leedcode 中均大量涉及。
建议在掌握线性查找的基础上,能够熟料掌握二分查找的使用方法,需要注意的是,二分查找仅仅适用于查找集合有序的情况,其进行一次查找的量级为 log n,与线性查找的 n 量级具有较大差距,虽然使用二分查找可能会需要额外的排序开销,但若待查找元素数量级过大时,使用线性查找很有可能会超时,二分查找可以在此时救场。以下给出二分查找的代码:
// 二分查找
bool BinarySearch(int n,int x){
int left = 0;
int right = n - 1;
while(left <= right){
int mid = (left + right) / 2;
if(arr[mid] < x){
left = mid + 1;
}else if(arr[mid] > x){
right = mid - 1;
}else{
return true;
}
}
return false;
}
字符串类型题目所考察的很细致,也是机试中考察频率较高的一个题目类型,可以用 char 数组来进行计算,也可以采用 c++ 中经过封装更为简单的STL 容器 string 来进行计算,建议从一开始就练习使用 string 数据类型,会比 char 方便很多。关于 string 的使用,可以参考此篇博文。
此类问题包含的题目形式比较多样,出题范围也很广,建议着重关注输入/输出格式,边界范围等细致内容。
如果使用暴力方法对求解字符串匹配问题,时间复杂度将达到 m * n(m n 分别为模式串与文本串的长度),但是使用KMP算法之后将使得问题的时间复杂度降低为 m + n。KMP算法的使用流程大致为 获得模式串的 next 数组 -》进行匹配 -》 返回结果,注意不要直接给数组命名next ,容易出现编译错误,可用大小写等区分。题型也基本集中于求第几位开始可以匹配、求匹配上的次数等,关键在于掌握构建next数组的方法以及匹配过程。下面是几道例题:北航上机题、上交上机题。
//KMP 求 next 数组的方法
int nextTable[10000];
void getNextTable(string pattern){
int j = 0;
nextTable[j] = -1;
int i = nextTable[j];
int m = pattern.size();
while(j < m){
if(i == -1 || pattern[j] == pattern[i]){
i++;
j++;
nextTable[j] = i;
}else{
i = nextTable[i];
}
}
return ;
}
// KMP 匹配的方法
int KMP(string text,string pattern){
int m = text.size();
int n = pattern.size();
getNextTable(string pattern);
int i = 0;
int j = 0;
while(i < m && j < n){
if(j == -1 || text[i] == pattern[j]){
i++;
j++;
}else{
j = nextTable[j];
}
}
if(j == n){
return i - j + 1; //返回开始匹配的位置
}else{
return -1;
}
}
本章主要介绍线性数据结构在机试题中的常见考察形式。
本质为可变长数组,具体的使用方法可点击此篇博文。主要在无法确定数组大小的时候使用,参见清华大学上机题感受一下。
队列的具体使用方法可点击此篇博文。使用队列可以很轻松的解决约瑟夫问题等先进先出的问题,要仔细观察题目意思。
栈的具体使用方法可点击此篇博文。栈的应用场景相对来说比较广泛,比如模式匹配、表达式求值等,可查看上交复试上机题(表达式求值)、吉林大学(堆栈的使用)。
此类问题通常不涉及很深奥的算法和数据结构,只与数理逻辑相关,典型的算法有进制转换、最大公约数与最小公倍数、质数、分解质因数、快速幂、矩阵与矩阵快速幂、高精度整数等。
传统的进制转换问题求解并不难(此题采用传统求解方法)需要注意对 0 的处理,建议采用 do..while语句,此时不用对 0 进行特判。但若处理高精度的进制转换问题,需要参考高精度整数的运算方法,如清华大学上机题(将高精度十进制转为二进制),以及更近一步(将高精度十进制转为二进制并转为十进制),掌握这两种方法之后绝大部分的题都可以应对。高精度进制转换的关键代码:
#include
#include
#include
#include
using namespace std;
// 十进制转为 x 进制
string devide(string str,int x){
int remainder = 0;
for(int i = 0;i < str.size();i++){
int current = remainder * 10 + (str[i] - '0');
str[i] = current / x + '0';
remainder = current % x;
}
int pos = 0;
while(str[pos] == '0'){
pos++;
}
return str.substr(pos);
}
// x 进制转为十进制
string multiple(string str,int x){
int carry = 0;
for(int i = str.size() - 1;i >= 0;i--){
int current = x * (str[i] - '0') + carry;
str[i] = current % 10 + '0';
carry = current / 10;
}
if(carry != 0){
str = '1' + str;
}
return str;
}
string add(string str,int x){
int carry = x;
for(int i = str.size() - 1;i >= 0;i--){
int current = (str[i] - '0') + carry;
str[i] = current % 10 + '0';
carry = current / 10;
}
if(carry != 0){
str = '1' + str;
}
return str;
}
int main(){
string str;
while(cin >> str){
vector o;
while(str.size() != 0){
int last = str[str.size() - 1] - '0';
o.push_back(last % 2);
str = devide(str,2);
}
string answer = "0";
for(int i = 0;i < o.size();i++){
answer = multiple(answer,2);
answer = add(answer,o[i]);
}
cout << answer << '\n';
}
return 0;
}
原理比较简单,只需掌握一个求解最大公约数的关键方法,最小公倍数为两数相乘再除以最大公约数即可。
求解最大公约数的递归算法:
int gcd(int a,int b){
if(b == 0){
return a;
}else{
return gcd(b, a % b);
}
}
素数问题涉及到素数的判定、素数表的生成等。素数的判定一般采用求根值的方法,判断是否含有小于等于sqrt(n) 的因数,注意要从 2 开始,素数表的生成一般采用“埃氏筛法”,用到的代码如下:
const int maxn = 100010;
int flag[maxn] = {0};
vector prime;
for(int i = 2;i < maxn;i++){
if(flag == 0) prime.push_back(i);
for(int j = i + i;j < maxn;j += i){
flag[j] = 1;
}
}
质因子分解可能会统计输入数字的质因数个数等,一般的方法是首先生成质数表,质数表生成时的 maxn 上限在 输入数据 n的规模不太大时可以直接设置为 n ,但一般情况下 n 的范围都比较大,此时为了防止数组越界,将 maxn 的值设置为 sqrt(n) + 1,在计算时遍历除以所有的质数表后如果 n 还不是 1,那么直接 ans + 1即可。理论依据: n 至多只存在一个大于 sqrt()的质因数
清华大学--质因数的个数
N.1 单点测试和多点测试
单、多点测试有时候平台会直接给出,比如PAT 普遍采用单点测试,每一个测试文件仅包含一个测试用例,通过一个会获得相应的分数,codeup 等大多数OJ都是采用多点测试,一个测试文件包含多个测试用例,全部通过才会获得相应的分数。通俗来说,单点测试写的是一次性程序,需要执行多次程序获得每一个结果,多点测试写的是可循环程序,只要反复执行程序的核心部分。
在考试的过程中,很多题目会说明输入数据会包含多个测试用例,此时必须要采用多点测试的写法,若题目中没有明确采用单点测试,建议也按照多点测试来写。
多点测试的几种写法:
// 1. while-EOF 用于不知道几个用例,不知道什么时候结束。
while(scanf("%d",&n) != EOF){
// 核心部分
}
// 2. while - break 知道结束条件时用,比如输入 0 时结束
while scanf("%d",&n) != EOF){
if(n == 0) break;
// 核心部分
}
// 3 . while(T--) 知道一共有几组用例时
while(T --){
// 核心部分
}
上篇结束,
其他专题请点击:搜索专题,图论专题,动态规划专题。