zoukankan      html  css  js  c++  java
  • 图论模板

    加了部分博客链接

    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

    未完待续

  • 相关阅读:
    021.day21 反射 Class类 反射常用操作
    020.day20 线程概述 多线程优缺点 线程的创建 线程常用方法 生命周期 多线程同步
    019.day19 缓冲流 对象流 标准输入输出流
    018.day18 map集合如何实现排序 File类 IO流 字节流 字符流 编码
    017.day17 Map接口 克隆 treeSet集合排重缺陷
    016.day16 HashSet TreeSet 比较器Comparable Comparator
    015.day15
    014.day14
    013.day13
    线程
  • 原文地址:https://www.cnblogs.com/OvOq/p/14853244.html
Copyright © 2011-2022 走看看