C++刷题秘籍

C++刷题秘籍
在经过一个暑假的刷题过程中(只刷了pat乙级和洛谷新手村),总结了一下一些很有用的知识和函数,了解这些可以再刷题的时候避免自己写一大堆不必要的代码,在做题的时候节省时间。不同的函数也可以开阔自己的解题思路,从而更快想出最佳的解答方法。如果有在用C++进行刷题的小伙伴可以看看。不足的地方也希望大家能够指出来,一起学习一起进步。

1、为什么用C++
C++的好处在于它和C语言一样是比较底层的语言,写代码的时候自由度很大,也就是你可以用最本质方法写程序。就好比你想造一辆车,用C++的话就等于用一个一个零件做,用面相对象语言的话就是等于用造好了的一些部位进行拼凑。造好了的东西一般不一定能符合题目要求,库函数又不好改,显然c和c++在刷题有很多便捷的地方。而且C++完全兼容C,很多C的东西也可以拿过来直接用,这点特别舒服,我的代码经常都是C混着C++写的。C++还有几个强大的类库vector map string,也就是这些类库的存在让我们不选择C,而是用C++,这点用过的人都说好。

2、输入
做题第一步,当然是输入啦。如果一个题目的数据连输入都不知道怎么输入的话,基本没法动手,一个好的输入代表这数据调用方便,层次也很清楚,并且节省内存。从pat的题目上看,近年来输入的难度也开始增大,也就是说数据量开始变大,让人不好处理。

cin:神器,在数据量少的时候用着特别爽,关键就是只要按三下键盘就完成了。比java输入方便太多了。cin在输入字符串的时候要注意,cin遇到空格啊回车啊制表符什么的都会停止,字符串有可能有空格的话一定不要用

getline:用于string类专门处理cin输入空格的制表符会停止的问题,语法为getline(cin,str);这种输入字符串的形式只有遇到回车才会停止,巨好用。但是有一个坑就是:如果在用getline输入字符串之前用了cin,必须要用一个getchar来吸收cin后面的回车符。

gets:用于C语言char类型字符串输入,也是专门处理遇到空格会停止的问题,基本和getline类似。

Scanf:C语言风格输入,这玩意巨牛逼,能帮你跳过一些不必要的输入。用这个函数的时候必须输入的和这个函数里面的格式对应,这不是一个坏处。可以让你把有用的数据提出来,因为有时候题目给的输入并不是所有都是要存起来的。举一个例子:
需要输入的是(a),但是有用的数据却是a,括号自然可以舍去。
scanf("(%c)",&a);这样写显的就很方便。
并且,在数据量很大的时候,scanf比cin速度快,本人亲测~

数字和字符串:这是两个很神奇的东西,比如输入12345678987654321,这到底是一个数字还是字符串,答案当然是看题目啦,题目如果可以让这个长度超过50位以上,尽量用字符串,long long也是有限的,而字符串基本可以说可以让数字无限大。字符串和数字还有很多很方便转换的函数,这些在后面会继续提到。

3、输出
cout:除了敲起来比printf快找不到任何优点。

printf:强大无比的输出函数,找不到任何缺点。
保留两位小数:%.2lf
输出正号:%+d
按5位数的格式输出 %05d 如0,在这个格式下输出00000
同样,在数据量大的时候printf比cout快

4、常用库函数
atoi:字符串转数字,用于c语言风格的字符串。这东西好用在忽略前面的0,也就是00038字符串在转化之后就成了38.
用法 atoi(str); 返回值为一个int
头文件:#include

sort:排序函数,底层据说是用快速排序写的,所以速度上是很快的。学会这个函数还有必要傻傻的两个循环去写冒泡或者选择排序了吗?显然是不存在的。这个函数有三个参数,第三个参数也可以不写,不写的话默认是升序。第一个参数是数组的首地址,第二个参数是数组的结束地址,第三个参数是一个函数。sort的精髓就是会写第三个参数函数。
用法 sort(str,str+n,cmp);
头文件:#include
升序:

int cmp(int a,int b){
	return a>b;
}

sort还有一个要掌握的地方,多重条件排序。
例如一个结构体,里面有名字,班级,成绩。题目给出的排序条件是:先按照成绩的降序排名,如果成绩一样按照班级编号升序排名,班级一样再按照名字的升序排名。

typedef struct stu
{
    int score;
    int class1;
    string name;

}stu;
bool cmp(stu a,stu b)
{
    if(a.score!=b.score)
    {
        return a.score>b.score;
    }
    else
    {
        if(a.class1!=b.class1)
        {
            return a.class1

这个比较方法很有用,希望大家都能掌握

#include

这个头文件里有一大堆处理字符串的方法
isalpha()判断是否为字母
isdight()判断是否为数字09(字符和数字09都返回true)
islower()判断是否为小写
tolower()转化字符为小写
isupper()判断是否为大写
toupper()转化为大写

sprintf:这是一个数字转字符串函数,从字面意思上可以理解为打印到string上。这个函数其实不止可以当做数字转字符串,用法巨多,有兴趣的自己百度研究吧,这个给一个简单的数字转字符串用法
sprintf(str,”%d”,123456);
第一个参数为字符串,第二个为格式占位符,可以想到,占位符有很多写法,这里也是可以的,所以说用法很多。第三个参数为数字。

#include:
数学函数,有一些还是要学会的。
Sin()正弦函数,同理一些应该都知道,这里不一一写出
Round()四舍五入,保留到整数
Sqrt()开根号
Abs()绝对值
Ceil()向上取整
Floor()向下取整

#include

这个库是c语言风格的字符串库,里面也有很多好用的函数
Strlen()测长度
Strcpy()复制,c语言风格的字符串复制一定要用这个,不要傻逼一样的直接=号。有一次上课,C++老师傻傻的给我们演示的时候用=号,还说”首地址复制过去了,没错啊”,我无语了半天,这个问题也让老师傻傻在上面想了半节课,希望大家不要犯这种错误。
Strcmp()比较字符串
还有很多,但是一般学了string就很少会用c语言风格的字符串,稍微了解一下就行。

5、类库
Vector:容器类库,vector和数组其实区别不是特别大。一般在数组长度不确定的时候我会用vector,因为vector不用开数组大小,还有就是二维数组我会选择用vector。当然,如果能记得vector里面的函数,并且这个函数能为解题带来方便,肯定首选vector
常用方法:
(1)a.assign(b.begin(), b.begin()+3); //b为向量,将b的0~2个元素构成的向量赋给a
(2)a.assign(4,2); //是a只含4个元素,且每个元素为2
(3)a.back(); //返回a的最后一个元素
(4)a.front(); //返回a的第一个元素
(5)a[i]; //返回a的第i个元素,当且仅当a[i]存在2013-12-07
(6)a.clear(); //清空a中的元素
(7)a.empty(); //判断a是否为空,空则返回ture,不空则返回false
(8)a.pop_back(); //删除a向量的最后一个元素
(9)a.erase(a.begin()+1,a.begin()+3); //删除a中第1个(从第0个算起)到第2个元素,也就是说删除的元素从a.begin()+1算起(包括它)一直到a.begin()+ 3(不包括它)
(10)a.push_back(5); //在a的最后一个向量后插入一个元素,其值为5
(11)a.insert(a.begin()+1,5); //在a的第1个元素(从第0个算起)的位置插入数值5,如a为1,2,3,4,插入元素后为1,5,2,3,4
(12)a.insert(a.begin()+1,3,5); //在a的第1个元素(从第0个算起)的位置插入3个数,其值都为5
(13)a.insert(a.begin()+1,b+3,b+6); //b为数组,在a的第1个元素(从第0个算起)的位置插入b的第3个元素到第5个元素(不包括b+6),如b为1,2,3,4,5,9,8 ,插入元素后为1,4,5,9,2,3,4,5,9,8
(14)a.size(); //返回a中元素的个数;
(15)a.resize(10); //将a的现有元素个数调至10个,多则删,少则补,其值随机
(16)a.resize(10,2); //将a的现有元素个数调至10个,多则删,少则补,其值为2
(17)a.reserve(100); //将a的容量(capacity)扩充至100,也就是说现在测试a.capacity();的时候返回值是100.这种操作只有在需要给a添加大量数据的时候才 显得有意义,因为这将避免内存多次容量扩充操作(当a的容量不足时电脑会自动扩容,当然这必然降低性能)
(18)a==b; //b为向量,向量的比较操作还有!=,>=,<=,>,<

Map:map其实是一一对应的集合,并不是想象中的图,map可以帮助我们完成一些非数字的散列。比如一个题目,需要根据string类型去查找,普通的数组无法模拟这个散列,map的用处就来了,map,就达到了用string找值的目的。String是下标,int是对应的值。例如有一个string类型的name,我要在里面保存数值2.可以写成:

map mapp;
Mapp[name]=2;

一般map使用迭代器遍历

for(iter = _map.begin(); iter != _map.end(); iter++) { 
cout << iter->first << " : " << iter->second << endl;
 }

first是下标,second是值
6、简单的算法知识
素数:做题的时候素数出现的概率很高很高,对素数进行判断或者加工的题目很多,总不能每次都现场推吧,一定要把判断素数的代码背下来

bool isprime(int n)
{
    int m = sqrt(n);
    for(int i=2;i<=m;i++)
    {
        if(n%i==0)
            return false;
    }
    return true;
}

这是一种速度比较好的判断素数方法,《编程珠玑》对判断素数的时间进行了很深入的解析,有兴趣的可以去了解一下

回文数:就是一种对称的数,如12345654321

int ishuiwen(int n)
{
    int sum=0;
    int k=n;
    while(n!=0) {
        sum=sum*10+n%10;
        n/=10;
    }
    if(sum==k)
        return 1;
    else
        return 0;
}

最大公约数/最小公倍数

int gcd(int a, int b){
    return b == 0 ? a : gcd(b, a % b);
}

返回值为最大公约数
a*b/返回值 为最小公倍数
这里给出一个问题:化简分数 输入 1/3 1/6 输出1/2

7、查找
查找怎么快?二分?二分太蠢了 ,二分还要排序,排序的复杂度起码是nlogn,多浪费时间啊。做题的话基本对时间的要求比较高,对内存要求很低。为什么?现在的机器的内存都很大很大,基本电脑都128g+1t了,内存放心用。但时间很重要啊,你开个网站等半分钟,简直想死的心情。所以做题查找一般用散列。(然而在某些特定的条件下还是得用二分

和数据结构书上的散列不同,刷题用的散列一般更暴力,完全不考虑内存,不必什么哈希探查什么的,能一一对应就行。这里用一个题目带大家理解这种思想

1038 统计同成绩学生(20 分)
本题要求读入 N 名学生的成绩,将获得某一给定分数的学生人数输出。
输入格式:
输入在第 1 行给出不超过 10​5​​ 的正整数 N,即学生总人数。随后一行给出 N 名学生的百分制整数成绩,中间以空格分隔。最后一行给出要查询的分数个数 K(不超过 N 的正整数),随后是 K 个分数,中间以空格分隔。
输出格式:
在一行中按查询顺序给出得分等于指定分数的学生人数,中间以空格分隔,但行末不得有多余空格。
输入样例:
10
60 75 90 55 75 99 82 90 75 50
3 75 90 88
输出样例:
3 2 0

这个题目可以用分数作为下标写一个数组,int array[101]={0};
第二行输入的分数,每个分数都当成下标
Cin>>temp;
Array[temp]++;
这样就保存了每个分数出现的次数,然后第三行输入的分数就直接在这个散列中找就行了。
如果直接把第二行的所有分数直接存在一个数组里,第三行的分数一个一个去上面那个数组里比较,这多麻烦啊,一定超时,用这种思想的话就避免了多次比较。
完整代码

#include 

using namespace std;

int main()
{
    int score[100001]={0};
    int num,k;
    int temp;
    cin >> num;
    for(int i=0;i> temp;
        score[temp]++;
    }
    cin >> k;
    int array1[k];
    for(int i=0;i>array1[i];
    }
    for(int i=0;i

这种做题的方法很重要,希望大家都能理解。

8、深度优先遍历
个人感觉不会深搜那你算法就不能算厉害,有些题目是一定要用深搜来处理的,如果不会的话,那些题目是没办法动手。虽然这个东西确实有点难,希望大家有时间一定要研究研究怎么写出来,光懂书上的概念是没有一点用的。
给出两个题目:蓝桥杯李白打酒,n个数的全排列。把这两个题目研究透了,简单的深搜就能写的。
李白打酒代码

#include
using namespace std;
int cont = 0;//步数
void dfs(int step,int jiu,int dian,int hua)
{
    if(step>14||jiu<0||dian<0||hua<1)//不满足条件
        return;
    if(step==14&&jiu==1&&dian==0&&hua==1)//找到一种满足条件的
    {
        cont++;
        return;
    }
    dfs(step+1,jiu-1,dian,hua-1);//遇到花
    dfs(step+1,jiu*2,dian-1,hua);//遇到店
}
int main()
{

    dfs(0,2,5,10);
    cout << cont;
    return 0;
}

n个数的全排列

#include

using namespace std;
int cont = 0;//步数
int visit[30]={0};
void dfs(int step,int n)
{
    if(step==n)
    {
        cont++;
        return;
    }
    for(int i=1;i<=n;i++)
    {
       if(!visit[i])
       {
           visit[i]=true;
           dfs(step+1,n);
           visit[i]=false;
       }
    }
}
int main()
{
    int n;
    cin >> n;
    dfs(0,n);
    cout << cont;
    return 0;
}

9、总结
暂时先写这么多,这些都是一些很有用的方法或者知识,把这些记住刷题就能少百度几次了。在线刷题都是黑盒测试,有些测试点真的是变态,所以做题目一定要多想特殊情况,把所有坑的数据都考虑好,并不是题目给的样例过了就行,希望大家记住这点。愿大家看完的我秘籍之后,刷题都能一次AC

你可能感兴趣的:(笔记)