• 图论模板


    加了部分博客链接

    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
    ");
        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

    未完待续

  • 相关阅读:
    ConvertUtils的理解
    mysql存储过程 详细注释
    线程方法
    集合的方法
    StringStringBufferStringBuilder
    Java基础知识点1
    Java基础知识点
    索引+sql优化
    Oracle数据库02
    Oracle数据库01
  • 原文地址:https://www.cnblogs.com/OvOq/p/14853244.html
Copyright © 2020-2023  润新知