我们的目标是,4A, 4A, 4A !!!
基本操作
C++ 常用头文件和宏定义
- 一了百了
#include <bits/stdc++.h>
- 大军来袭
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
- 宏定义
#define FF(a,b) for(int a=0;a<b;a++)
#define F(a,b) for(int a=1;a<=b;a++)
#define LEN 200
#define INF ((1<<30)-1)
#define bug(x) cout<<#x<<"="<<x<<endl;
// 把INF定义为(INF+INF)还能保证不溢出的一个数
/*
INF+INF=2147483646
0x7FFFFFFF=2147483647
*/
using namespace std;
typedef long long ll;
const double pi=acos(-1);
输入输出
- double
- 输入:
%lf
- 输出:
%f
- scanf
返回值解析:
- 返回正确读入的参数个数。
- 遇到文件结尾,返回
EOF
,也就是-1
- 安全的获取一行:
fgets (buf, MAX, stdin)
- printf
- 补前导0:
%02d
:总宽度为2,不足用0补 - 设置精度:
%.2f
保留两位小数
- 输入流输出流
- 关同步
std::ios::sync_with_stdio(false)
- 获取一行
cin.get(name,SIZE); //回车保留在输入队列中
cin.getline(name,SIZE); //回车被清除
- 设置输出精度
cout<<setprecision(2)<<a; //输出: 123.45
- 设置前导零:
cout<<setfill('0')<<setw(4)<<value<<endl;
二分
原生实现lower_bound和upper_bound
https://www.luogu.org/problemnew/show/P3366
- lower_bound
int l=0; //初始化 l ,为第一个合法地址
int r=10; //初始化 r , 地址的结束地址
int mid;
while(l<r) {
mid=(l+r)/2;
if(arr[mid]>=obj){
r=mid;
}else{
l=mid+1;
}
}
- upper_bound
int l=0; //初始化 l ,为第一个合法地址
int r=10; //初始化 r , 地址的结束地址
int mid;
while(l<r) {
mid=(l+r)/2;
if(arr[mid]>obj){
//没有=符号是与上文算法唯一的区别
r=mid;
}else{
l=mid+1;
}
}
排序
结构体排序
- 结构体内重载小于符号
bool operator < (const Node& obj) const
{
return d<obj.d; //从小到大排序
}
- 结构体外定义cmp函数
int cmp(const Node&a,const Node&b){
return a.d<b.d;//从小到大排序
}
多属性按优先级排序
int cmp(Node& a,Node& b) {
if(a.name!=b.name) //最高优先级
return a.name<b.name;
else if(this.id!=o.id)
return a.id-b.id;
else
return a.credit-b.credit;
}
图论
基本数据结构
链式前向星
- 数据结构定义
VN是题目指定的最大顶点数, EN是题目指定的最大边数
//---------------------链式前向星----------------------------
int head[VN]; //记录源点u在mp中第一个地址i=head[u] 调用完之后就可以用mp[i]访问边表mp
int cnt=0; //边表下标,随着数据的录入而扩张
struct edge{
//边
int to,next,w;
};
edge mp[EN*2]; //边表
void add(int u,int v,int w){
//增加边
mp[cnt].to=v;
mp[cnt].w=w;
mp[cnt].next=head[u]; //指向源点u所构成的静态链表的头结点。如果是首次构造链,head[u]=-1 ,相当于NULL
head[u]=cnt++; //更新当前地址
}
void init_mp(){
fill(head,head+VN,-1);
}
//---------------------链式前向星----------------------------
- 使用
首先必须在录入边表数据之前进行初始化
init_mp();
对于无向图,一定要注意对两个方向的顶点同时进行加边操作
FF(i,M){
int a,b,w;
scanf("%d%d%d",&a,&b,&w);
add(a,b,w);
add(b,a,w);
}
遍历: 对u的邻接点遍历, i 代表的是边表下标, to 代表邻接点, w 代表边权
for(int i=head[u];~i;i=mp[i].next){
//链式前向星遍历
int to=mp[i].to; //链式前向星: u 的后继点 to
int w=mp[i].w; //链式前向星: u -> to 边权
}
堆优化
- 数据结构定义
优先队列默认是大根堆
定义新的比较规则cmp, 重载priority_queue, 按dist[index]的值升序排列
//---------------------堆优化----------------------------
struct cmp{
bool operator () (int a,int b){
return dist[a]>dist[b];
}
};
priority_queue<int,vector<int>,cmp> pq;
//---------------------堆优化----------------------------
- 在dijkstra使用
下文所有有注释的都是相对于非堆优化, 需要增删的内容
fill(dist,dist+LEN,MAX);
dist[s]=0;
pq.push(s); //优先队列入队初始化
while(!pq.empty()){
//优先队列非空
int u=pq.top(); //寻找最小的dist[u]
pq.pop();
if(vis[u]) continue; //寻找最小的dist[u]
vis[u]=1;
for(int i=0;i<N;i++) if(!vis[i]){
if(dist[u]+g[u][i]<=dist[i]){
//一定要包含等于符号!!!!
dist[i]=dist[u]+g[u][i];
pq.push(i); //优先队列入队
}
}
}
最短路径
Floyd
void print_via_ij(int i,int j){
if(via[i][j]!=-1){
int k=via[i][j];
cout<<k<<"->";
print_via_ij(k,j);
}else{
cout<<j<<"->";
}
}
void floyd(){
for(int i=0;i<N;i++){
//初始化
for(int j=0;j<N;j++){
if(g[i][j]==0) g[i][j]=INF;
via[i][j]=-1;
A[i][j]=g[i][j];
}
}
for(int k=0;k<N;k++){
//临时点
for(int i=0;i<N;i++){
for(int j=0;j<N;j++){
if(A[i][j]>(A[i][k]+A[k][j])){
A[i][j]=A[i][k]+A[k][j];
via[i][j]=k;
}
}
}
}
//输出0到各点距离
int v=0;
for(int i=1;i<N;i++){
cout<<v<<"->";
print_via_ij(v,i);
cout<<endl;
}
}
原生dijkstra
https://www.luogu.org/problemnew/show/P1339
https://www.luogu.org/blog/TQCAI/solution-p1339
int N,M;
int dist[VN];
int pre[VN];
int vis[VN];
void dijkstra(int s){
fill(dist,dist+N,INF);
fill(pre,pre+N,-1);
dist[s]=0;
pq.push(s); //优先队列初始化
while(!pq.empty()){
//优先队列非空
int u=pq.top(); //找到最小u点
pq.pop();
if(vis[u]) continue;//找到最小u点
vis[u]=1; //找到最近点,加入S集
for(int i=head[u];~i;i=mp[i].next){
//链式前向星遍历
int to=mp[i].to; //链式前向星: u 的后继点 to
int w=mp[i].w; //链式前向星: u -> to 边权
if(!vis[to]){
//松弛
if(dist[to] >= dist[u]+w) {
//s->to >= s->u->to,一定要包含等于符号
dist[to] = dist[u] + w;
pre[to]=u;
pq.push(to);
}
}
}
}
}
最短路计数
int pathc[VN]; //最短路条数
void dijkstra(int s,int e){
pathc[s]=1; //初始化源点最短路条数
...................
if(!vis[to]){
//松弛
if(dist[to] > dist[u]+w) {
//s->to > s->u->to
pathc[to] = pathc[u];
//其余公操作
}else if(dist[to]==dist[u]+w){
pathc[to] += pathc[u];
//其余公操作
}
}
在有多条最短路的情况下, 寻找点权途经点权最大的最短路
int vw[VN]; //点权 vertex weight
int by_vw[VN]; //途经点权 bypass vertex weight
void dijkstra(int s,int e){
by_vw[s]=vw[s];//初始化源点途经点权
...................
if(!vis[to]){
//松弛
if(dist[to] > dist[u]+w) {
//s->to > s->u->to
by_vw[to]=by_vw[u]+vw[to];
//其余公操作
}else if(dist[to]==dist[u]+w){
if(by_vw[u]+vw[to] > by_vw[to]){
by_vw[to]=by_vw[u]+vw[to];
}
}
}
最小生成树
https://www.luogu.org/problemnew/show/P3366
https://www.luogu.org/blog/TQCAI/solution-p3366
并查集优化Kruskal
//--------------------------并查集-----------------------------
int fa[LEN];
int init(){
int i;
FF(i,LEN) fa[i]=i;
}
int findFa(int x){
if(x==fa[x]) return x;
int r=x;
while(r!=fa[r]){
//找到根节点
r=fa[r];
}
int t=x;
while(x!=fa[x]){
//路径压缩
t=fa[x];
fa[x]=r;
x=t;
}
return r;
}
void Union(int a,int b){
int pa=findFa(a);
int pb=findFa(b);
fa[pa]=pb;
}
//--------------------------边表-----------------------------
typedef struct Edge{
//边表
int u,v,w;//两点和边权
//按照边权对边表进行排序
bool operator < (const Edge& obj) const
{
return w<obj.w; //从小到大排序
}
}Edge;
Edge edge[200010];
//--------------------------主函数-----------------------------
init(); //一定要初始化,不然拉闸
int cnt=0,mst=0;
FF(i,M){
scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
}
sort(edge,edge+M);
FF(i,M){
Edge &e=edge[i];
//Union操作
int pa=findFa(e.u);
int pb=findFa(e.v);
if(pa==pb)
continue;
fa[pa]=pb;
//更新MST
mst+=e.w;
cnt++;
}
if(cnt==N-1){
printf("%d\n",mst);
}else{
printf("orz\n")