OJ模板

这篇博客主要介绍了C++编程中的基础操作,包括常用头文件、宏定义、输入输出、二分查找和排序算法。此外,还涵盖了图论、数据结构如链式前向星、堆优化、最小生成树,以及动态规划、搜索法和数学问题的解决策略。内容详实,适合C++初学者和进阶者学习。
摘要由CSDN通过智能技术生成

我们的目标是,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
  1. 输入:%lf
  2. 输出:%f
  • scanf
    返回值解析
  1. 返回正确读入的参数个数。
  2. 遇到文件结尾,返回EOF,也就是-1
  3. 安全的获取一行:fgets (buf, MAX, stdin)
  • printf
  1. 补前导0:%02d:总宽度为2,不足用0补
  2. 设置精度:%.2f保留两位小数
  • 输入流输出流
  1. 关同步
std::ios::sync_with_stdio(false)
  1. 获取一行
cin.get(name,SIZE);			//回车保留在输入队列中
cin.getline(name,SIZE);		//回车被清除
  1. 设置输出精度
cout<<setprecision(2)<<a;	//输出: 123.45
  1. 设置前导零:
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")
  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值