图论【模板】

1.图的存储:

邻接矩阵: g[a][b]存储点a,b间的有关信息(权值或该两点间是否有边)稠密图
邻接表: 类似哈希表的拉链法,每个点都有一个单链表,存储这个点可以走到的点(包括直接走到和间接走到)。
邻接表的结构体实现

struct node{
    int value;//存储边的权值
    int to;//存储该边的终点
    int next;//存储下一条边的编号
}a[N];
int cnt=0;
int head[N];//存储以i为起点的边的编号
void add(int u,int v,int value){
    a[cnt].to=v;
    a[cnt].value=value;
    a[cnt].next=head[u];
    head[u]=cnt++;//以当前点为起点的编号为cnt
    //无向图,正反各存一遍
    a[cnt].to=u;
    a[cnt].value=value;
    a[cnt].next=head[v];
    head[v]=cnt++;
}

链式前向星:

int h[N],e[N],ne[N],idx;
void init(){
    idx=0;
    memset(h,-1,sizeof h);
}
void add(int a,int b){
    e[idx]=b,ne[idx]=h[a],h[a]=idx,idx++;
}

2.图的深度优先遍历和广度优先遍历

3.拓扑排序

有向无环图=拓扑图
手写队列

int h[N],e[N],ne[N],idx,ans=N,n,m;
int d[N],q[N];
void init(){
    idx=0;
    memset(h,-1,sizeof h);
}
void add(int a,int b){
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
bool topsort(){
    int hh=0,tt=-1;
    for(int i=1;i<=n;i++)
        if(!d[i]) q[++tt]=i;
    while(hh<=tt){
        int t=q[hh++];
        for(int i=h[t];i!=-1;i=ne[i]){
            int j=e[i];
            if(--d[j]==0) q[++tt]=j;
        }
    }
    return tt==n-1;
}

stl queue写法

4.最短路算法

最短路问题分为单源最短路和多源汇最短路。前者是求某一点到其他点的最短距离,后者是起点终点不确定,任选一组起点终点求最短路。
n点m边

Dijkstra
所有边权均是正数的单源最短路 O(nn)
适用于稠密图 即m和n
n接近

int n,m;///n个点 m条边
int g[maxn][maxn];///邻接矩阵建图
int dis[maxn];///记录当前点到起点的距离
bool st[maxn];///若当前点的最短距离已经确定 为true

int dijkstra(){
    memset(dis,0x3f,sizeof dis);///初始化距离为正无穷
    dis[1]=0;///从1开始更新
    for(int i=0;i<n-1;i++){
        int t=-1;
        ///找到当前不在st中而且距离最小的点t
        for(int j=1;j<=n;j++)
            if(!st[j]&&(t==-1||dis[t]>dis[j]))
                t=j;
        ///用点t更新其他不在st中的点的距离
        for(int j=1;j<=n;j++)
            dis[j]=min(dis[j],dis[t]+g[t][j]);
        ///将t点放到st集合里
        st[t]=true;
    }
    ///如果该点距离为正无穷 说明没有更新过
    if(dis[n]==0x3f3f3f3f) return -1;
    return dis[n];
}

堆优化的Dijkstra
所有边权均是正数的单源最短路 O(mlogn)
适用于稀疏图 即m和n一个数量级

int n,m;
int h[maxn],w[maxn],e[maxn],ne[maxn],idx;
int dis[maxn];
bool st[maxn];

void add(int a,int b,int c){
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int dijkstra(){
    memset(dis,0x3f,sizeof dis);
    dis[1]=0;
    ///建立一个维护最小值的优先队列
    priority_queue<PII,vector<PII>,greater<PII>>heap;
    heap.push({0,1});///起始点放入队列
    while(heap.size()){
        auto t=heap.top();///最小值
        heap.pop();
        int ver=t.second,d=t.first;
        if(st[ver]) continue;///该点更新
        st[ver]=true;
        for(int i=h[ver];i!=-1;i=ne[i]){
            int j=e[i];
            if(dis[j]>d+w[i]){
                dis[j]=d+w[i];
                heap.push({dis[j],j});
            }
        }
    }
    if(dis[n]==0x3f3f3f3f) return -1;
    return dis[n];
}

Bellman Ford
存在负权边/限制边数 O(n*m)
适合于m和n一个数量级

struct node{
    int a,b,c;
}e[maxx];
int n,m,k;
int dis[maxn],last[maxn];///保证更新只是从上一次更新,不会被以前的影响 
void BellmanFord(){
    memset(dis,0x3f,sizeof dis);
    dis[1]=0;
    for(int i=0;i<k;i++){
        memcpy(last,dis,sizeof dis);
        for(int j=0;j<m;j++){
            auto t=e[j];
            dis[t.b]=min(dis[t.b],last[t.a]+t.c);
        }
    }
}

SPFA
存在负权边 (相当于队列优化的Bellman Ford)

int n, m;
int h[N], w[N], e[N], ne[N], idx;
int dist[N];
bool st[N];
void add(int a, int b, int c)
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
int spfa()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;

    queue<int> q;
    q.push(1);
    st[1] = true;

    while (q.size())
    {
        int t = q.front();
        q.pop();

        st[t] = false;

        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (dist[j] > dist[t] + w[i])
            {
                dist[j] = dist[t] + w[i];
                if (!st[j])
                {
                    q.push(j);
                    st[j] = true;
                }
            }
        }
    }
    return dist[n];
}

Floyd
Floyd 多源汇 O(n^3) 最短路

int n,m,q;
int d[maxn][maxn];
void init(){
	for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(i==j) d[i][j]=0;
            else d[i][j]=inf;
}
void Floyd(){
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
}

4.最小生成树算法

prim算法

int n, m;
int g[N][N];
int dist[N];
bool st[N];
int prim(){
    memset(dist, 0x3f, sizeof dist);

    int res = 0;
    for (int i = 0; i < n; i ++ )
    {
        int t = -1;
        for (int j = 1; j <= n; j ++ )
            if (!st[j] && (t == -1 || dist[t] > dist[j]))
                t = j;

        if (i && dist[t] == INF) return INF;

        if (i) res += dist[t];
        st[t] = true;

        for (int j = 1; j <= n; j ++ ) dist[j] = min(dist[j], g[t][j]);
    }

    return res;
}

kruskal
O(mlogm)


int n,m;
int root[maxn];
struct node{
    int a,b,w;
}a[maxn];
bool cmp(node a,node b){
    return a.w<b.w;
}
int Find(int x){
    if(x!=root[x]) root[x]=Find(root[x]);
    return root[x];
}
int kruskal(){
    sort(a,a+m,cmp);
    for(int i=1;i<=n;i++) root[i]=i;
    int res=0,cnt=0;
    for(int i=0;i<m;i++){
        int aa=a[i].a;
        int b=a[i].b;
        int w=a[i].w;
        aa=Find(aa),b=Find(b);
        if(aa!=b){
            root[aa]=b;
            res+=w;
            cnt++;
        }
    }
    if(cnt<n-1) return INF;
    else return res;
}

5.染色法判定二分图

链式前向星存图

#include<bits/stdc++.h>
using namespace std;
const int maxn=200010;
int n,m;
int h[100010],e[maxn],ne[maxn],idx;
int col[100010];

void add(int a,int b){
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

bool dfs(int u,int c){
    col[u]=c;
    for(int i=h[u];~i;i=ne[i]){
        int j=e[i];
        if(!col[j]){
            if(!dfs(j,3-c)) return 0;
        }
        else if(col[j]==c) return 0;
    }
    return 1;
}

int main(){
    cin>>n>>m;
    memset(h,-1,sizeof h);
    while(m--){
        int x,y;
        cin>>x>>y;
        add(x,y);add(y,x);///无向图
    }
    bool flag=1;
    for(int i=1;i<=n;i++)
        if(!col[i]){
            if(!dfs(i,1)){
                flag=0;
                break;
            }
        }
    if(flag) puts("Yes");
    else puts("No");
    return 0;
}

邻接矩阵存图
偷的学长的(超小声)

vector<int>v[maxn];///vector存个图
int vis[maxn];///vis标记颜色
ll n,m;
bool dfs(int u,int c)
{
    vis[u]=c;
    int sz=v[u].size();
    for(int i=0;i<sz;i++)
    {
        int e=v[u][i];
        if(vis[e]==c) return false;
        if(!vis[e]&&!dfs(e,-c)) return false;
    }
    return true;
}
int main()
{
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=m;i++){
        int x,y;scanf("%d%d",&x,&y);
        v[x].push_back(y);
        v[y].push_back(x);
    }
    for(int i=1;i<=n;i++)///图可能不全是联通的 判断全部子图均为二分图才可以
        if(vis[i]==0&&!dfs(i,1)){
            printf("No");
            return 0;
        }
    printf("Yes\n");
    return 0;
}

最大流

#pragma GCC optimize(2)
#pragma GCC optimize("Ofast","unroll-loops","omit-frame-pointer","inline")
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll,ll>PLL;
typedef pair<int,int>PII;
#define I_int ll
#define modl 19260817*19890604-19491001
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
char F[200];
inline void out(I_int x) {
    if (x == 0) return (void) (putchar('0'));
    I_int tmp = x > 0 ? x : -x;
    if (x < 0) putchar('-');
    int cnt = 0;
    while (tmp > 0) {
        F[cnt++] = tmp % 10 + '0';
        tmp /= 10;
    }
    while (cnt > 0) putchar(F[--cnt]);
    //cout<<" ";
}

const int maxn=1e6+7;///总点个数,随题意改

const int MAX=1<<26;///最大值,随题意改

///链式前向星存储边
///表示u->v 边权为c(容量) 下一条边
struct Edge{
    ll u,v,c,ne;
};

///求最大流
struct Dinic{
    int n,m;///点数,边数
    int edn;///建图时所用边数
    int p[maxn];///链式前向星存图的父节点
    int d[maxn];///分层建图时表示的层数
    int sp,tp;///原点 汇点
    Edge edge[maxn*6];///存储边

    ///初始化
    void init(int sp,int tp){
        ///this->sp=sp;//可省去
       /// this->tp=tp;
        edn=0;///清空建图时计边的计数器
       ///   memset(p,-1,sizeof p);
      memset(p,-1,sizeof(int)*(n+2));///小优化 仅初始化使用的空间
    }

    void addedge(int u,int v,int c){///建图加边
        edge[edn]={u,v,c,p[u]};p[u]=edn++;///正向一定要加的
        edge[edn]={v,u,0,p[v]};p[v]=edn++;///无向图改成c 反悔边
    }
    ///分层建图并且寻找增广路的过程
    int bfs(){
        queue<int>q;
        while(!q.empty()) q.pop();///清空队列
        memset(d,-1,sizeof d);///初始化距离数组
        d[sp]=0;q.push(sp);///进行分层建图

        while(!q.empty()){
            int cur=q.front();q.pop();
            for(int i=p[cur];~i;i=edge[i].ne){
                int u=edge[i].v;
                if(d[u]==-1&&edge[i].c>0){///容量>0才会有贡献
                    d[u]=d[cur]+1;
                    q.push(u);
                }
            }
        }
        return d[tp]!=-1;///是否存在增广路 
    }

    ll dfs(ll a,ll b){
        ll r=0;
        if(a==tp) return b;///到达汇点
        for(int i=p[a];~i&&r<b;i=edge[i].ne){
            int u=edge[i].v;
            if(edge[i].c>0&&d[u]==d[a]+1){
                ///只拓展下一层 并且容量>0才会有贡献
                int x=min(edge[i].c,b-r);///可以增加的流量
                x=dfs(u,x);
                r+=x;///统计流量
                
                ///更新边权:找到反向边
                ///奇数异或1相当于-1,偶数异或1相当于+1
                edge[i].c-=x;///回溯时更新
                edge[i^1].c+=x;///成对变换
            }
            ///if(!r) break;
        }
       if(!r) d[a]-=2;///uncertain
        return r;
    }

    ll Maxflow(){
        ll total=0,t;
        while(bfs()){
            while(t=dfs(sp,MAX)) total+=t;///增广找到流量
        }
        return total;
    }

}dinic;

int main(){
    dinic.n=read(),dinic.m=read();
    dinic.sp=read(),dinic.tp=read();
    dinic.init(dinic.sp,dinic.tp);
    for(int i=1;i<=dinic.m;i++){
        ll u=read(),v=read(),w=read();
        dinic.addedge(u,v,w);
    }
    ll t=dinic.Maxflow();
    cout<<t;
    return 0;
}

一般图最大匹配(带花树算法)

struct edge{
    int e,ne;
};

struct Flower{
    queue<int>q;
    edge e[maxn*maxn];
    int h[maxn],root[maxn],mat[maxn],pre[maxn],vis[maxn],dfn[maxn];
    int n,m,idx,timetemp,res;
    
    inline void init(){
        timetemp=res=idx=0;
        memset(h,0,sizeof h);
    }
    
    inline void add(int u,int v){
        e[++idx]={v,h[u]};h[u]=idx;
    }

    inline int Find(int x){
        if(x!=root[x]) root[x]=Find(root[x]);
        return root[x];
    }

    inline int LCA(int u,int v){
        ++timetemp;u=Find(u),v=Find(v);
        while(dfn[u]!=timetemp){
            dfn[u]=timetemp;
            u=Find(pre[mat[u]]);
            if(v) swap(u,v);
        }
        return u;
    }

    void Blossom(int x,int y,int w){
        while(Find(x)!=w){
            pre[x]=y;y=mat[x];
            if(vis[y]==2) vis[y]=1,q.push(y);
            if(x==Find(x)) root[x]=w;
            if(y==Find(y)) root[y]=w;
            x=pre[y];
        }
    }

    int Aug(int s){
        if((res+1)*2>n) return 0;
        for(int i=1;i<=n;i++) root[i]=i,vis[i]=pre[i]=0;
        while(!q.empty()) q.pop();
        q.push(s);vis[s]=1;
        while(!q.empty()){
            int u=q.front();q.pop();
            for(int i=h[u];i;i=e[i].ne){
                int v=e[i].e;
                if(Find(u)==Find(v)||vis[v]==2) continue;
                if(!vis[v]){
                    vis[v]=2;pre[v]=u;
                    if(!mat[v]){
                        for(int x=v,lst;x;x=lst) lst=mat[pre[x]],mat[x]=pre[x],mat[pre[x]]=x;
                        return 1;
                    }
                    vis[mat[v]]=1;q.push(mat[v]);
                }
                else{
                    int w=LCA(u,v);
                    Blossom(u,v,w);Blossom(v,u,w);
                }
            }
        }
        return 0;
    }
};

LCA

struct LCA
{
    int fa[maxn][25], dep[maxn];
    int e[maxn],ne[maxn],m;
    int h[maxn], idx = 0,n,root;
    void add(int a,int b){
    	e[idx]=b,ne[idx]=h[a],h[a]=idx++;///链式前向星存图
	}
    void init()
    {
        memset(h, -1, sizeof h);
        idx = 0;
        for(int i=1;i<n;i++){
        	int u=read,v=read;
        	add(u,v);add(v,u);
        }
    }
    void bfs(int root)
    {
        queue<int>q;
        memset(dep, 0x3f, sizeof dep);
        dep[0] = 0; ///特殊处理
        dep[root] = 1;
        q.push(root);
        while(!q.empty())
        {
            int t = q.front();
            q.pop();
            for(int i = h[t]; ~i; i = ne[i])
            {
                int j = e[i];
                if(dep[j] > dep[t] + 1)
                {
                    dep[j] = dep[t] + 1;
                    q.push(j);
                    fa[j][0] = t; ///预处理fa数组
                    for(int k = 1; k <= 15; k++)
                        fa[j][k] = fa[fa[j][k - 1]][k - 1];
                }
            }
        }
    }

    int lca(int a, int b)
    {
        if(dep[a] < dep[b]) swap(a, b);
        for(int k = 15; k >= 0; k--)
            if(dep[fa[a][k]] >= dep[b]) a = fa[a][k]; ///使a,b跳到同一层
        if(a == b) return a;
        for(int k = 15; k >= 0; k--)
            if(fa[a][k] != fa[b][k]) a = fa[a][k], b = fa[b][k];
        return fa[a][0];
    }
};

部分模板来自AcWing

未完待续

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

豆沙睡不醒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值