编程比赛常用技巧总结
我这里的编程比赛主要指的是算法类的比赛,例如蓝桥杯,天梯赛,CCF之类的比赛。
为了备战天梯赛、蓝桥杯而写的一个总结。
输入输出
加快输入输出
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin,cout之所以效率低,是因为先把要输出的东西存入缓冲区,再输出,导致效率降低,而这段语句可以来打消iostream的输入输出缓存,可以节省许多时间,使效率与scanf与printf相差无几(但是好像还是scanf、printf比较快)
读入一行字符串
getline(cin,str)
常用输出格式控制
用来控制保留几位小数
setiosflags(ios::fixed) 控制是否是小数
setprecision(2)控制保留几位小数
cout<<setiosflags(ios::fixed)<<setprecision(2)<<e<<"%";
设置宽度 及填充对应字符
setw(5)<<setfill('0')
比赛常用算法
四舍五入
如果忘记了四舍五入的函数的话
int r(double a)
{
int b;
if(a>0)
{
return (a*2+1)/2;
}
else
{
return (a*2-1)/2;
}
}
绝对值
fabs()//用于求浮点数绝对值
abs()//用于求整形的绝对值
快速幂算法
求 m^k mod p,时间复杂度 O(logk)。
k相当于转成2进制,根据最后以为判断是否应该乘
int qmi(int m, int k, int p)
{
int res = 1 % p, t = m;
while (k)
{
if (k&1) res = res * t % p;
t = t * t % p;
k >>= 1;
}
return res;
}
最短路算法
int g[N][N]; // 存储每条边
int dist[N]; // 存储1号点到每个点的最短距离
bool st[N]; // 存储每个点的最短路是否已经确定
// 求1号点到n号点的最短路,如果不存在则返回-1
int dijkstra()
{
memset(dist, 0x3f, sizeof dist);//注意是赋值的为0x3fmemset赋值是按字节赋值
dist[1] = 0;
for (int i = 0; i < n - 1; i ++ )//第一个点已经找到所以循环n-1
{
int t = -1; // 在还未确定最短路的点中,寻找距离最小的点
for (int j = 1; j <= n; j ++ )
if (!st[j] && (t == -1 || dist[t] > dist[j]))
t = j;
// 用t更新其他点的距离
for (int j = 1; j <= n; j ++ )
dist[j] = min(dist[j], dist[t] + g[t][j]);
st[t] = true;
}
if (dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
}
并查集
(1)朴素并查集:
int p[N]; //存储每个点的祖宗节点
// 返回x的祖宗节点
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
// 初始化,假定节点编号是1~n
for (int i = 1; i <= n; i ++ ) p[i] = i;
// 合并a和b所在的两个集合:
p[find(a)] = find(b);
(2)维护size的并查集:
int p[N], size[N];
//p[]存储每个点的祖宗节点, size[]只有祖宗节点的有意义,表示祖宗节点所在集合中的点的数量
// 返回x的祖宗节点
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
// 初始化,假定节点编号是1~n
for (int i = 1; i <= n; i ++ )
{
p[i] = i;
size[i] = 1;
}
// 合并a和b所在的两个集合:
size[find(b)] += size[find(a)];
p[find(a)] = find(b);
(3)维护到祖宗节点距离的并查集:
int p[N], d[N];
//p[]存储每个点的祖宗节点, d[x]存储x到p[x]的距离
// 返回x的祖宗节点
int find(int x)
{
if (p[x] != x)
{
int u = find(p[x]);
d[x] += d[p[x]];
p[x] = u;
}
return p[x];
}
// 初始化,假定节点编号是1~n
for (int i = 1; i <= n; i ++ )
{
p[i] = i;
d[i] = 0;
}
// 合并a和b所在的两个集合:
p[find(a)] = find(b);
d[find(a)] = distance; // 根据具体问题,初始化find(a)的偏移量
二叉树建树
例如:
输入前序遍历、中序遍历
输出层序遍历
一直操作左右两边便好
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<queue>
#define tree node*
using namespace std;
struct node{
int d;
tree l;
tree r;
};
const int N=40;
int z[N],q[N],n;
tree Build(int ql,int qr,int zl,int zr)
{
if(ql>qr) return NULL;
tree root=new node;
root->d=q[ql];
int k;
for(int i=zl;i<=zr;i++)
{
if(z[i]==q[ql])
{
k=i;
}
}
int leftnum=k-zl;
root->l=Build(ql+1,ql+leftnum,zl,k-1);
root->r=Build(ql+leftnum+1,qr,k+1,zr);
return root;
}
void cen(tree t)
{
int i=0;
queue<tree> q;
q.push(t);
while(!q.empty())
{
tree f=q.front();
i++;
if(i==n)
{
cout<<f->d;
}
else
{
cout<<f->d<<" ";
}
q.pop();
if(f->r!=NULL)
{
q.push(f->r);
}
if(f->l!=NULL)
{
q.push(f->l);
}
}
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
cin>>z[i];
}
for(int i=0;i<n;i++)
{
cin>>q[i];
}
tree t=Build(0,n-1,0,n-1);
cen(t);
}
结构体重载小于号
用于设置map<node,int> 类型的数据
bool operator<(const People &t) const
{
if(time!=t.time) return time<t.time;
else return idx<t.idx;
}
数据结构
堆
默认是大根堆
想要转成小根堆还有是读入的数转化为负数,输出的时候输出正数就好。
priority_queue, 优先队列,默认是大根堆
size()
empty()
push() 插入一个元素
top() 返回堆顶元素
pop() 弹出堆顶元素
定义成小根堆的方式:priority_queue<int, vector<int>, greater<int>> q;
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void printHeap(vector<int> &v){
for(vector<int>::iterator it= v.begin();it!=v.end();++it){
cout<< *it <<" ";
}
cout<<"\n"<<endl;
}
int main()
{
vector<int> min={10,30,22,6,15,9};
//建立小顶堆
make_heap(min.begin(), min.end(), greater<int>());
printHeap(min);//6 10 9 30 15 22
//插入元素
min.push_back(20);
push_heap(min.begin(),min.end(), greater<int>());//该算法前提:必须在堆的条件下
printHeap(min); //6 10 9 30 15 22 20 仍为小顶堆
//删除堆顶元素
pop_heap(min.begin(),min.end(), greater<int>());
printHeap(min);//9 10 20 30 15 22 6 不为小顶堆 这个pop_heap操作后,实际上是把堆顶元素放到了末尾
min.pop_back();//这才彻底在底层vector数据容器中删除
printHeap(min);//9 10 20 30 15 22 仍为小顶堆
//堆排序 保持greater,小顶堆,得到的是降序
sort_heap(min.begin(),min.end(), greater<int>());//试了用less,结果杂乱无章
printHeap(min);//30 22 20 15 10 9 注意结果是降序的哦!!!其实是调用了很多次pop_heap(...,greater..),每一次都把小顶堆堆顶的元素往末尾放,没放一次end迭代器减1
return 0;
}
pair
pair<int, int>
first, 第一个元素
second, 第二个元素
支持比较运算,以first为第一关键字,以second为第二关键字(字典序)
vector
vector, 变长数组,倍增的思想
size() 返回元素个数
empty() 返回是否为空
find(v.begin(), v.end(), a) ,如果能在容器v中不能寻找到a,则返回v.end()
clear() 清空
front()/back()
push_back()/pop_back()
begin()/end()
[]
支持比较运算,按字典序
set, map, multiset, multimap, 基于平衡二叉树(红黑树),动态维护有序序列
size()
empty()
clear()
begin()/end()
++, -- 返回前驱和后继,时间复杂度 O(logn)
set/multiset
insert() 插入一个数
find() 查找一个数
set_name.find(element)
count() 返回某一个数的个数
erase()
(1) 输入是一个数x,删除所有x O(k + logn)
(2) 输入一个迭代器,删除这个迭代器
lower_bound()/upper_bound()
lower_bound(x) 返回大于等于x的最小的数的迭代器
upper_bound(x) 返回大于x的最小的数的迭代器
map/multimap
insert() 插入的数是一个pair
erase() 输入的参数是pair或者迭代器
find()
[] 注意multimap不支持此操作。 时间复杂度是 O(logn)
lower_bound()/upper_bound()
deque
效率低
size()
empty()
clear()
front()/back()
push_back()/pop_back()
push_front()/pop_front()
begin()/end()
[]
stack
stack, 栈
size()
empty()
push() 向栈顶插入一个元素
top() 返回栈顶元素
pop() 弹出栈顶元素
queue
queue, 队列
size()
empty()
push() 向队尾插入一个元素
front() 返回队头元素
back() 返回队尾元素
pop() 弹出队头元素