图论模板整理

图论模板

链式前向星

cpp 复制代码
const int N=2e5+7;
int h[N],e[N],ne[N],w[N];
int idx=0;
void init(){
    memset(h,-1,sizeof h);
}
void add(int a,int b,int c){
    e[idx]=b;
    w[idx]=c;
    ne[idx]=h[a];
    h[a]=idx++;
}

Hamilton型问题和Eular型问题

在蓝桥杯中,我们经常可以看到Hamilton限制(每个点只能走一次)和Eular限制(每条边只能走一次)的问题,在16届蓝桥杯的压轴题中给出的正是Eular型限制,对于具体的问题而言,正解的方式会有所不同,但是我们可以总结出利用暴力搜索来解决该类问题的模板。

也就是(Eular型遍历:每条边只走一次)(Hamilton型遍历,每个点只走一次)

Eular型遍历

存储:

cpp 复制代码
int h[N],e[N],ne[N],idx;//存储图
int vt[N];// 用来记录边的访问情况
int pr[N];//用来记忆每条边的反向边是哪条边

函数:

cpp 复制代码
int h[N],e[N],ne[N],idx;//存储图
int vt[N];// 用来记录边的访问情况
int pr[N];//用来记忆每条边的反向边是哪条边
void init(){}//初始化h
int add(int a,int b)//添加边返回边的编号
void dfs(int x,int edge)//执行dfs操作,参数表示通过边edge到达的点x

模板:

cpp 复制代码
void init(){
    memset(h,-1,sizeof h);
}
int add(int a,int b){
    e[idx]=b;
    ne[idx]=h[a];
    h[a]=idx++;
    return idx-1;
}
void dfs(int x,int edge){//表示现在通过边edge到达了点x
    vt[edge]=true;
    vt[pr[edge]]=true;
    if (满足题目某种条件){
        //执行题目中的要求记忆方案数记忆最大值...
    }
    for (int i=h[x];~i;i=ne[i]){
        int j=e[i];
        if (vt[i])
            continue;
        dfs(j,i);
    }
    vt[edge]=false;
    vt[pr[edge]]=false;
}
int main(){
    //加边操作时这样修改
    int a,b;
    scanf("%d%d",&a,&b);
    int x=add(a,b);
    int y=add(b,a);
    pr[x]=y;
    pr[y]=x;
}

Hamilton型遍历

本文暂且不表

树相关问题

求depth和p

利用链式前向星对树进行存储后,我们没有树的深度信息和父节点信息。我们可以选定一个根节点,通过dfs求解出depth(每个节点的深度)和p(每个节点的父母)

存储:

int h[N],e[N],ne[N],idx;

int depth[N],p[N];

函数:

void dfs(int u,int p){

//直接用一遍dfs结束depth和p的运算

}

模板:

cpp 复制代码
int h[N],e[N],ne[N],idx=0;
int depth[N],p[N];
void add(int a,int b){
    e[idx]=b;
    ne[idx]=h[a];
    h[a]=idx++;
}
add(a,b);
add(b,a);
void dfs(int x,int f){
    p[x]=f;
    for (int i=h[x];i!=-1;i=ne[i]){
        int j=e[i];
        if (j==f){
            continue;
        }
        depth[j]=depth[x]+1;
        dfs(j,x);
    }
}

倍增求LCA

这里我们介绍求LCA的倍增算法

倍增算法求LCA就是说利用一个记忆数组f[i][j]来记录
对于每个节点i而言,其向上2^j个祖先是谁
倍增LCA的实现需要在刚才使用dfs求出每个节点深度的同时
递推求出f数组的值

具体操作见下述dfs

cpp 复制代码
void dfs(int now,int parent){
	deepth[now]=deepth[parent]+1;
	f[now][0]=parent;
	for (int i=1;i<=lg[deepth[now]]-1;i++){
		f[now][i]=f[f[now][i-1]][i-1];
	}
	for (int i=h[now];~i;i=ne[i]){
		if (e[i]!=parent)
			dfs(e[i],now);
	} 
}

利用刚才预处理得到的f数组我们就可以直接O(logn)进行查询

cpp 复制代码
// 预处理 (你的笔记版本)
lg[1] = 0;
for (int i = 2; i <= n; i++) lg[i] = lg[i / 2] + 1;

// LCA 里的跳跃逻辑
int LCA(int x, int y) {
    if (depth[x] < depth[y]) swap(x, y);
    
    // 跳到同一高度
    while (depth[x] > depth[y]) {
        // 因为你的 lg[i] 已经是指数了,直接用即可
        x = f[x][lg[depth[x] - depth[y]]]; 
    }
    
    if (x == y) return x;
    
    // 一起向上跳 (注意这里建议依然用从大到小的 for 循环)
    for (int i = lg[depth[x]]; i >= 0; i--) {
        if (f[x][i] != f[y][i]) {
            x = f[x][i];
            y = f[y][i];
        }
    }
    return f[x][0];
}

最短路算法

Floyd算法

Floyd算法是在邻接矩阵上运行的最短路算法,需要手动取最小值来处理重边问题,可以解决负边权的问题,但是不能解决有负环的问题。时间复杂度为O(n^3)

cpp 复制代码
//Floyd
/*代码实现中需要注意一下两点:
1.矩阵初始化为inf,g[i][i]初始化为0
2.松弛操作的k需要是三重循环中的第一重
*/
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<set>
#include<map>
#include<string>
#include<cstring>
#include<vector>


using namespace std;

int n,m;
const int N=105;
int g[N][N];
int main(){
    memset(g,0x3f3f3f3f,sizeof g);
    cin>>n>>m;
    for (int i=1;i<=n;i++){
        g[i][i]=0;
    }
    while (m--){
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        g[u][v]=g[v][u]=min(g[u][v],w);
    }
    for (int k=1;k<=n;k++){
        for (int i=1;i<=n;i++){
            for (int j=1;j<=n;j++){
                    g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
            }
        }
    }   
    for (int i=1;i<=n;i++){
        for (int j=1;j<=n;j++){
            cout<<g[i][j]<<' ';
        }
        cout<<endl;
    }
    return 0;
}
cpp 复制代码
//Dijkstra算法(堆优化Ologn)
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<queue>


using namespace std;
typedef pair<int ,int > PII;
const int N=100010;
const int M=200010;
const int INF=0x3f3f3f3f;
int n,m,s;
bool vis[N];
int dist[N];
int h[M],e[M],w[M],ne[M],idx=1;
priority_queue<PII ,vector<PII> ,greater<PII> > heap;
void add(int a,int b,int c){
	e[idx]=b;
	ne[idx]=h[a];
	w[idx]=c;
	h[a]=idx++;
}

void dijkstra(){
	memset(dist,INF,sizeof (dist));
	dist[s]=0;
	heap.push({0,s});
	while (!heap.empty()){
		PII t=heap.top();
		heap.pop();//这句不能忘记写 
		int ver=t.second;
		if (vis[ver]) continue;
		vis[ver]=true;
		int distance=t.first;
		for (int i=h[ver];i;i=ne[i]){ //遍历图的时候要记住变量是h[ver] 
			int j=e[i];
			if (dist[j]>w[i]+distance){ //注意这里写的是w[i]+distance 
				dist[j]=distance+w[i];
				if (!vis[j])
					heap.push({dist[j],j});
			}
		}
	}
}
int main(){
	cin>>n>>m>>s;
	for (int i=1;i<=m;i++){
		int a,b,c;
		cin>>a>>b>>c;
		add(a,b,c);
	}
	dijkstra();
	for (int i=1;i<=n;i++)	
		cout<<dist[i]<<' ';
	return 0; 
}
cpp 复制代码
//SPFA算法(BellmanFold队列优化版本)
# include <bits/stdc++.h>
using namespace std;
int n , m , s , u , v , w , dis[10010];
bool vis[10010];
vector <pair <int , int> > g[10010];
void bfs()
{
    for(int i = 1 ; i <= n ; i ++)
    {
        dis[i] = 1e9;
    }
    queue <int> q;
    q.push(s);
    vis[s] = 1;
    dis[s] = 0;
    while(!q.empty())
    {
        int t = q.front();
        q.pop();
        vis[t] = 0;
        for(int i = 0 ; i < g[t].size() ; i ++)
        {
            if(dis[g[t][i].first] > dis[t] + g[t][i].second)
            {
                dis[g[t][i].first] = dis[t] + g[t][i].second;
                if(vis[g[t][i].first])
                {
                    continue;
                }
                vis[g[t][i].first] = 1;
                q.push(g[t][i].first);
            }
        }
    }
    return ;
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m >> s;
    for(int i = 1 ; i <= m ; i ++)
    {
        cin >> u >> v >> w;
        g[u].push_back({v , w});
    }
    bfs();
    for(int i = 1 ; i <= n ; i ++)
    {
        if(dis[i] == 1e9)
        {
            cout << (1LL << 31) - 1 << " ";
        }
        else
        {
            cout << dis[i] << " ";
        }
    }
    return 0;
}

最小生成树算法(MST)

Kruskal算法

luoguP3366[模板]最小生成树

cpp 复制代码
//算法流程:
/*
1.将所有边排序
2.从小到大遍历所有边,如果这个边与之前选择的边不构成圈,则选择这个点
3.否则,continue
*/
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>

using namespace std;
const int N=4e5+7;
int n,m;
struct edge{
    int a;
    int b;
    int c;
    bool friend operator < (edge a,edge b){
        return a.c<b.c;
    }
};
int h[N];
edge e[N];
int p[N];
int r[N];
int find(int x){
    if (x==p[x])    
        return x;
    return p[x]=find(p[x]);
}
void merge(int a,int b){
    int roota=find(a);
    int rootb=find(b);
    if (roota==rootb)
        return ;
    if (r[roota]<r[rootb]){
        p[roota]=rootb;
        r[rootb]+=r[roota];
    }
    else {
        p[rootb]=roota;
        r[roota]+=r[rootb];
    }
}
void init(){
    memset(h,-1,sizeof h);
    for (int i=1;i<=n;i++){
        p[i]=i;
        r[i]=1;
    }
}
int main(){
    cin>>n>>m;
    init();
    for (int i=1;i<=m;i++){
        int a,b,c;
        cin>>a>>b>>c;
        e[i].a=a;
        e[i].b=b;
        e[i].c=c;
    }
    sort(e,e+m);
    int cnt=0;
    int sum=0;
    for (int i=1;i<=m;i++){
        int roota=find(e[i].a);
        int rootb=find(e[i].b);
        if (roota==rootb)
            continue;
        merge(e[i].a,e[i].b);
        cnt++;
        sum+=e[i].c;
        if (cnt==n-1){
            cout<<sum<<endl;
            return 0;
        }
    }
    if (cnt<n-1)
        cout<<"orz"<<endl;
    return 0;
}

对于最大瓶颈问题,我们只需要跑一遍最小生成树,在跑的同时记录树中边权最小的边即可。

相关推荐
超级大只老咪2 小时前
线性递推通用模板
java·开发语言·算法
17(无规则自律)2 小时前
DFS:带重复项的全排列,程序运行全流程解析
c++·算法·深度优先
AI棒棒牛2 小时前
SCI核心论文剖析:ICSD-YOLO:面向工业现场安全的实时智能检测算法
算法·yolo·目标检测·计算机视觉·目标跟踪·yolo26
郝学胜-神的一滴2 小时前
「栈与缩点的艺术」二叉树前序序列化合法性判定:从脑筋急转弯到工程实现
java·开发语言·数据结构·c++·python·算法
汀、人工智能3 小时前
[特殊字符] 第25课:合并两个有序链表
数据结构·算法·链表·数据库架构··合并两个有序链表
Hello.Reader3 小时前
双卡 A100 + Ollama 生产部署从安装、踩坑、调优到最终可上线方案
linux·人工智能·算法
计算机安禾3 小时前
【数据结构与算法】第30篇:哈希表(Hash Table)
数据结构·学习·算法·哈希算法·散列表·visual studio
xiaoye-duck3 小时前
《算法题讲解指南:动态规划算法--子序列问题(附总结)》--32.最长的斐波那契子序列的长度,33.最长等差数列,34.等差数列划分II-子序列
c++·算法·动态规划