2022暑假ACM集训周记(持续更新~)

2022暑假ACM集训周记

Week1

Day1

C++头文件

#include<bits/stdc++.h>  //万能头文件
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include <iostream>      // 引入流输入输出库
using namespace std;
cin>>n;					 //cin输入
cout<<n<<endl;			 //cout输出,endl换行

不定量输入

while(~scanf("%d",&a));				//ctrl-z结束输入
while(scanf("%d",&a)!=EOF);
while(scanf("%d",&a)!=-1);
while(scanf("%d",&a)!=2;
      
//读取n输入
scanf("%d",&n);
while(n--);
      
//0结束
while(scanf("%d",&n)&&(a));

浮点数陷阱

#include<stdio.h>
int main()
{	
    doublei;
    for(i=0;i!=10;i+=0.1)
    printf("%.1lf\n",i);
    return 0;
}
/*说明:对于i可以达到10.0,但永远不会与10相等,所以for循环是一个死循环。
对于float和dobule类型的数据不能直接用==和!=来比较大小,即不要测试精确的浮
点型数值,需要用精度比较,来测试一个可接受的数值范围。如:*/

//for(i=0;fabs(10-i)>1e-5;i+=0.1)

OJ各类错误提示

Accepted 通过

Presentation Error 输出格式错误。

Wrong Answer 答案错误。

Runtime Error 程序运行时发生错误,多为数组访问越界。

Time Limit Exceeded 超内存错误,程序运行使用的内存超过限制的内存用量。

Compile Error 编译错误,源代码中有语法错误。

Day2

memcpy函数

void *memcpy(void *dest, void *src, unsigned int count);		

使用memcpy函数要包含头文件string.h
从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中

e.g.

//从数组a复制k个元素到数组b(int)
	memcpy(b,a,sizeof(int)*k);
//浮点(double)
	memcpy(b,a,sizeof(double)*k);
//把数组a全部复制到数组b中
	memcpy(b,a,sizeof(a));

memset函数

void *memset(void *buffer,char c,int count);

buffer所指内存区域的前count个字节设置成字符c,第三个参数指的是字节的个数,返回指向buffer的指针

e.g.

memset(a,0,sizeof(a))	//把a数组的所有字节赋为0,比for方便

正确使用memset - 王陸 - 博客园 (cnblogs.com)

getchar()介绍

int getchar(void);

getchar函数的返回值是用户输入的第一个字符的ASCII码,如出错返回-1

可以利用getchar()函数让程序调试运行结束后等待编程者按下键盘才返回编辑界面

用法:在主函数结尾return 0之前加上getchar()

#include <stdio.h>
int main(void)
{
	for(int i=1;i<=10;i++)
		printf("%d ",i);
	getchar();		//用以暂停程序
	return 0;
}

gets()

char *gets(char *buffer);

stdio流中读取字符串,直至接受到换行符或EOF时停止,并将读取的结果存放在buffer指针所指向的字符数组中

换行符不作为读取串的内容,读取的换行符被转换为null值,并由此来结束字符串

返回值:

  • 读入成功,返回与参数buffer相同的指针;读入过程中遇到EOF(End-of-File)或发生错误,返回NULL指针
  • 所以在遇到返回值为NULL的情况,要用ferror或feof函数检查是发生错误还是遇到EOF

注 意:

  • 本函数可以无限读取,不会判断上限,所以程序员应该确保buffer的空间足够大,以便在执行读操作时不发生溢出
  • 如果溢出,多出来的字符将被写入到堆栈中,这就覆盖了堆栈原先的内容,破坏一个或多个不相关变量的值
  • 为了避免这种情况,我们可以用fgets()来替换gets()

C语言中的gets(s)存在缓冲区溢出漏洞,不推荐使用

isupper()

extern int isupper(int c);

判断字符c是否为大写英文字母:小写return 0;

islower()

int islower(int c)

检查参数c是否为小写英文字母:小写return true;else return 0;

toupper()

extern int toupper(int c);

将字符c转换为大写英文字母:返回大写字母

tolower()

extern int tolower(int c);

将字符c转换为小写英文字母:返回小写字母

指针

void my_swap(int &a, int &b)    // 和指针等价实现的交换函数
{
    int t = a;
    a = b;
    b = t;
}

Day3

hypot()

double hypot(double x, double y);

可以求三角形的斜边长

assert()

#include<assert.h>		//head

ASSERT()是一个调试程序时经常使用的宏,在程序运行时它计算括号内的表达式。

  • 如果表达式为 FALSE (0), 程序将报告错误,调用库函数abort()并终止执行。
  • 如果表达式不为 0,则继续执行后面的语句。
  • 这个宏通常原来判断程序中是否出现了明显非法的数据,如果出现了终止程序以免导致严重后果,同时也便于查找错误。

作用域和生存期

作用域

  • 变量的作用范围(全局变量和局部变量)

生存期

  • 变量从被生成到被撤消的这段时间,即变量占用内存的时间。

image-20220719091514778

递归调用

递归计算n!

int fac(int n){
	if(n==1)return 1;
	return fac(n-1)*n;
}

求组合数(高效递归版)

题图2.jpg

函数原型

double Cmb(int x, int y);

说明:x 和 y 为非负整数,且 x ≥ y ≥ 0,函数值为组合数 Cxy

要求:不要使用循环语句,不调用阶乘函数和排列数函数。找出最快的递推公式,该函数直接调用自己求得结果。

#include <stdio.h>

double Cmb(int x, int y);

int main()
{
    int m, n;
    scanf("%d%d", &m, &n);
    printf("%.10g\n", Cmb(m, n));
    return 0;
}

/* 你提交的代码将被嵌在这里 */
输入样例 输出样例
0 0 1
5 0 1
34 17 2333606220
160 158 12720
1000 100 6.385051193e+139

source code

double Cmb(int x, int y){
    if(x-y<y)
        y=x-y;
    if(x==y||y==0)
        return 1;
    return x*1.0/y*Cmb(x-1,y-1);
}

bool函数

bool n=true;
bool m=false;
//n=1,m=0;
bool flag=1>2;
//1>2为假(0),所以flag=0;
bool temp=2>1;
//2>1为真(!0),所以temp=1;

Day4

复杂度

STL分为三类

  • 容器(container)
  • 算法(algorithm)
  • 迭代器(iterator)

Day5

STL container(容器)

<vector>
<list>
<deque>
<set>
<stack>
<queue>
<map>
<unordered_map>

image-20220721090140510

queue

#include<queue>
//queue<Type,Container>
queue<int>q1;
queue<double>q2;
queue<char>q3;
//默认为用deque容器实现的queue;
queue<char,list<char>>q1;
//用list容器实现的queue;
queue<int, deque<int>>q2;
//用deque容器实现的queue 

stack

#include<stack>
stack<int>q;
int x;
q.push(x);	//x存入栈顶
q.top();	//返回栈顶元素
q.pop();	//删除栈顶元素
q.size();	//返回栈元素个数
q.empty();	//判断栈是否为空,空返回true,非空返回false

vector

<vector>

  • 动态数组,运行时根据需要改变数组大小
  • 以数组形式存储,内存空间是连续的,索引可以在常数时间内完成。
  • 但是在中间进行插入和删除操作,会造成内存块的拷贝。

所谓动态增加大小,并不是在原空间之后接续新空间(因为无法保证原空间之后尚有可供配置的空间),而是以原大小的两倍另外配置一块较大空间,然后将原内容拷贝过来,然后才开始在原内容之后构造新元素,并释放原空间。因此,对vector的任何操作,一旦引起空间的重新配置,指向原vector的所有迭代器就都失效了。

在这里插入图片描述

#include<vector>
//定义具有10个整型元素的向量(尖括号为元素类型名,它可以是任何合法的数据类型),不具有初值,其值不确定
vector<int>a(10);

//定义具有10个整型元素的向量,且给出的每个元素初值为1
vector<int>a(10,1);

//用向量b给向量a赋值,a的值完全等价于b的值
vector<int>a(b);

//将向量b中从0-2(共三个)的元素赋值给a,a的类型为int型
vector<int>a(b.begin(),b.begin+3);

//从数组中获得初值
int b[7]={1,2,3,4,5,6,7};
vector<int> a(b,b+7);

//定义结构性数组              
struct point {int x,y;};
vector<point>a;			//a用来存坐标

//在向量尾部添加元素1              
a.push_back(1);

//元素个数
a.size();

//判断是否为空
a.empty();

//删除尾部元素
a.pop_back();

//清空
a.clear();

//反转数组
reverse(a.begin(),a.end());

//删除元素1
a.erase(1);
 
//多维数组
vector<int>a[max];
//第一维大小是固定的MAXN,第二维是动态的              

sort()

#include<algorithm>
const int N=1e5;
//O(nlogn)
void sort (RandomAccessIterator first, RandomAccessIterator last);
void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);
//排序范围[first,last)不包括last
int a[N];
bool cmp(int a,int b){
    return a<b;			//定义从大到小排序
}
sort(a,a+n);		//默认从小到大排序a
sort(a,a+n,cmp)		//自定义排序准则

string

#include<string>

string s;	//创建对象
s="Hello"	//赋值
string s1=s.length();	//获取字符串长度
string s1=s.size();	//获取字符串长度
string s3=s1+s2;		//字符串连接
//查找串
//find-从指定位置起向后查找,直到串尾

//从第一个字符开始查找,返回首次匹配的位置
cout << s.find('e') << endl;	//1	初始位置0

//从第3个字符开始查找
cout << s.find('l',3) << endl;	//3

//
cout << s.find(s.begin(0,l.end(),'o'));

//倒置串
reverse(s.begin(), s,end());

//string比较
s.compare();		//和strcmp类似

s.substr(1,3)		//返回1~3的元素
    
//插入
s.insert(3,"a")		//在s[3]前插入a

//删除指定元素
s.erase()

find()

auto

Day6

set

STL的set用二叉搜索树实现

  • set中的元素都是排好序的
  • set集合中没有重复的元素
#include<set>
set<int>a;
a.insert(item);		//把item放入set
a.begin()         //返回set容器第一个元素的迭代器
a.end()      //返回一个指向当前set末尾元素的下一位置的迭代器.
a.clear()          //删除set容器中的所有的元素
a.empty()    //判断set容器是否为空
a.max_size()   //返回set容器可能包含的元素最大个数
a.size()      //返回当前set容器中的元素个数
a.rbegin()     //返回的值和end()相同
a.rend()     //返回的值和begin()相同

map

next_permutation()

swap()

list

相较于vector的连续线性空间,list就显得负责许多,它的好处是每次插入或者删除一个元素,就是配置或者释放一个元素的空间。因此,list对于空间的运用有绝对的精准,一点也不浪费。而且,对于任何位置的元素插入或元素的移除,list永远是常数时间。

在这里插入图片描述

List容器是一个双向链表

  • 采用动态存储分配,不会造成内存浪费和溢出
  • 链表执行插入和删除操作十分方便,修改指针即可,不需要移动大量元素
  • 链表灵活,但是空间和时间额外耗费较大

List容器不能像vector一样以普通指针作为迭代器,因为其节点不能保证在同一块连续的内存空间上。List迭代器必须有能力指向list的节点,并有能力进行正确的递增、递减、取值、成员存取操作。所谓”list正确的递增,递减、取值、成员取用”是指,递增时指向下一个节点,递减时指向上一个节点,取值时取的是节点的数据值,成员取用时取的是节点的成员。

//list数据元素插入和删除操作

push_back(elem);//在容器尾部加入一个元素
pop_back();//删除容器中最后一个元素
push_front(elem);//在容器开头插入一个元素
pop_front();//从容器开头移除第一个元素
insert(pos,elem);//在pos位置插elem元素的拷贝,返回新数据的位置。
insert(pos,n,elem);//在pos位置插入n个elem数据,无返回值。
insert(pos,beg,end);//在pos位置插入[beg,end)区间的数据,无返回值。
clear();//移除容器的所有数据
erase(beg,end);//删除[beg,end)区间的数据,返回下一个数据的位置。
erase(pos);//删除pos位置的数据,返回下一个数据的位置。
remove(elem);//删除容器中所有与elem值匹配的元素。

//list赋值操作

assign(beg, end);//将[beg, end)区间中的数据拷贝赋值给本身。
assign(n, elem);//将n个elem拷贝赋值给本身。
list& operator=(const list &lst);//重载等号操作符
swap(lst);//将lst与本身的元素互换。

//list数据的存取
front();//返回第一个元素。
back();//返回最后一个元素。

posted @ 2022-07-16 12:05  HeadmasterEggy  阅读(112)  评论(0)    收藏  举报