zoukankan      html  css  js  c++  java
  • 4.29--4.30 图论

    1、二叉堆的操作

    (1)取出元素

    (2)插入元素

    (3)删除元素

    //手写大根堆维护 
    #include<bits/stdc++.h>
    using namespace std;
    int heap[1000];
    int up(int x)
    {
        if(heap[x]<=heap[x/2])
        return 0;
        else
        {
            swap(heap[x],heap[x/2]);
            up(x/2);
        }
    }
    int down(int x)
    {
        if(heap[x]>=heap[x*2]&&heap[x]>=heap[x*2+1])
        return 0;
        else
        if(heap[x*2]>heap[x*2+1])
        {
            swap(heap[x],heap[x*2]);
            down(x*2);
        }
        else
        {
            swap(heap[x],heap[x*2+1]);
            down(x*2+1);
        }
    }
    int m,od,x,n;
    int main()
    {
        scanf("%d",&m);
        while(m--)
        {
            scanf("%d",&od);
            if(od==1)
            {
                printf("%d",heap[0]);
            }
            if(od==2)
            {
                scanf("%d",&x);
                n++;
                heap[n]=x;
                up(n);
            }
            if(od==3)
            {
                heap[0]=heap[n];
                n--;
                down(1);
            }
        }
        return 0;
    }
    手写大根堆
    #include<bits/stdc++.h>
    using namespace std;
    priority_queue<int>heap;
    int m,x,od;
    int main()
    {
        scanf("%d",&m);
        while(m--)
        {
            scanf("%d",&od);
            if(od==1)
            printf("%d",heap.top());
            if(od==2)
            {
                scanf("%d",&x);
                heap.push(x);
            }
            if(od==3)
            {
                heap.pop();
            }
        }
        return 0;
    }
    大根堆优先队列维护

    2、最小生成树

    (1)prim算法 蓝白点思想

    #include<bits/stdc++.h>
    using namespace std;
    
    const int maxn=10000+15;
    int m,n,x,y,z,maxx,ans;
    int edge[maxn][maxn],dis[maxn];
    bool boo[maxn]; 
    int main()
    {
     scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&x,&y,&z);//加边 
            edge[x][y]=edge[y][x]=z;
        }
        memset(dis,127/3,sizeof(dis));
        dis[1]=0;boo[1]=1;//由结点1开始生成最小生成树,boo[1]=1,表示结点1已经加入最小生成树 
        for(int i=2;i<=n;i++){
            if(edge[1][i])
            dis[i]=min(dis[i],edge[1][i]);//更新和结点1相连的点的值 
        }
        for(int i=1;i<=n-1;i++){//生成n-1条边 
            maxx=0;
            for(int j=1;j<=n;j++){
                if(!boo[j])//找一个点没加入最小生成树并且距离已经生成的树的距离最小的 
                if(!maxx||dis[maxx]>dis[j])
                maxx=j;
            }
            boo[maxx]=1;//将它加入最小生成树 
            ans+=dis[maxx];//加入答案 
            for(int j=1;j<=n;j++){//将它更新其他点到最小生成树的距离; 
                if(!boo[j])
                if(edge[maxx][j])
                dis[j]=min(dis[j],edge[maxx][j]);
            }
        }
        printf("%d",ans);
        return 0;
    }
    prim
    //prim的队列优化 
    #include <bits/stdc++.h>
    using namespace std;
    
    const int maxn=100000+15;
    const int maxm=100000+15;
    struct Edge
    {
        int x,y,z,next;
        Edge(int x=0,int y=0,int z=0,int next=0):x(x),y(y),z(z),next(next) {}
    }edge[maxm*2];
    const bool operator < (const Edge &a,const Edge &b) //为了优先队列取边时,将权值最小的边先取出 
    {
        return a.z>b.z;
     } 
    int n,m;
    int sumedge,head[maxn];
    int ins(int x,int y,int z)
    {
        edge[++sumedge]=Edge(x,y,z,head[x]);
        return head[x]=sumedge;
    }
    priority_queue <Edge> que;//优先队列 里面的内容是一个结构体 是没条边的x,y,z,next; 
    int ans;
    bool boo[maxn];//标记有没有加入最小生成树 
    int main()
    {
        scanf("%d%d",&n,&m);//输入n个点m条边 
        for (int i=1;i<=m;i++)
        {
            int x,y,z;//输入起点,终点,权值 
            scanf("%d%d%d",&x,&y,&z);
            ins(x,y,z);//链表建边 
            ins(y,x,z);
        }
        memset(boo,false,sizeof(boo));//没有加入最小生成树 
        boo[1]=true;//结点1加入最小生成树;从结点1开始生成最小生成树; 
        for (int u=head[1];u;u=edge[u].next) que.push(edge[u]);//将和1相连的边入队 
        for (int i=1;i<n;i++)    //总共有n-1条边 
        {
            Edge temp;
            temp=que.top();//取出距离1结点长度最小的边 
            for (;boo[temp.y];que.pop(),temp=que.top());//当这个边的终点已经加入最小生成树,就删除这条边,取次小的边  
            que.pop();//删除这条边 已经加入最小生成树 留着也没用 
            ans+=temp.z;//加上这条边的权值 
            boo[temp.y]=true;//这条边的终点加入最小生成树 temp是一个结构体 
            for (int u=head[temp.y];u;u=edge[u].next)//扫一遍和现在的点相连的边 
             if (!boo[edge[u].y]) que.push(edge[u]);//将没有在最小生成树中的边入队; 
        }
        printf("%d
    ",ans);
        return 0;
     }
    prim队列优化

    (2)kruskal 贪心 并查集

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=100000;
    int n,m,x,y,z,far[maxn],ans;
    const int oo=1234567
    struct Edge
    {
        int x,y,z;
         Edge(int x=0,int y=0,int z=oo):
          x(x),y(y),z(z) {}
    }edge[maxn];
    bool cmp(Edge a,Edge b)
    {
        return a.z<b.z;
     } 
    int f(int x)
    {
        return far[x]==x?x:far[x]=f(far[x]);
    }
    void unionn(int x,int y)
    {
        far[f(x)]=f(y);
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&x,&y,&z);
            edge[i]=Edge(x,y,z);//这样对结构体赋值要在结构体里写个Edge(int ------) 
        }
        sort(edge+1,edge+m+1,cmp);
        for(int i=1;i<=n;i++)
        {
            far[i]=i;
        }
        for(int i=1;i<=m;i++)
        {
            int p=edge[i].x,q=edge[i].y;
            if(f(p)!=f(q)){
                unionn(p,q);
                ans+=edge[i].z;
            }
        }
        printf("%d",ans);
        return 0;
    }
    kruskal

    3、最小瓶颈生成树

    最小瓶颈生成树为使所有的生成树中最大权值最小

    最小生成树一定是最小瓶颈生成树

    最小瓶颈生成树不一定是最小生成树

    4、最优比率生成树

    二分思想

    改日再添

    5、最短路径

    (1)dijkstra

    #include <bits/stdc++.h>
    using namespace std;
    
    const int maxn=100000+15;
    const int maxm=100000+15;
    struct Edge
    {
        int x,y,z,next;
        Edge(int x=0,int y=0,int z=0,int next=0):x(x),y(y),z(z),next(next) {}
    }edge[maxm*2];
    const bool operator < (const Edge &a,const Edge &b)
    {
        return a.z>b.z;
     } 
    int n,m;
    int sumedge,head[maxn];
    int ins(int x,int y,int z)
    {
        edge[++sumedge]=Edge(x,y,z,head[x]);
        return head[x]=sumedge;
    }
    priority_queue <Edge> que;
    int ans;
    bool boo[maxn];
    int main()
    {
        scanf("%d%d",&n,&m);
        for (int i=1;i<=m;i++)
        {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            ins(x,y,z);
            ins(y,x,z);
        }
        memset(boo,false,sizeof(boo));
        boo[1]=true;
        for (int u=head[1];u;u=edge[u].next) 
          que.push(edge[u]);
        for (int i=1;i<n;i++)    //总共有n-1条边 
        {
            Edge temp;
            temp=que.top();
            for (;boo[temp.y];que.pop(),temp=que.top());
            que.pop();
            ans+=temp.z;
            boo[temp.y]=true;
            for (int u=head[temp.y];u;u=edge[u].next)
             if (!boo[edge[u].y]) que.push(edge[u]);
        }
        printf("%d
    ",ans);
        return 0;
     } 
    dijkstra

    (2)bellman-ford

    #include <bits/stdc++.h>
    using namespace std;
    
    const int maxn=1000;
    const int maxm=10000;
    const int oo=100000000;
    int n,m,x[maxm],y[maxm],z[maxm];
    int dis[maxn];
    int main()
    {
        scanf("%d%d",&n,&m);
        for (int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&x[i],&y[i],&z[i]);
        }
        for (int i=2;i<=n;i++) dis[i]=oo;
        for (int i=1;i<n;i++)
         for (int j=1;j<=m;j++)
          if (dis[x[j]]!=oo && dis[y[j]]>dis[x[j]]+z[j])
           dis[y[j]]=dis[x[j]]+z[j];
        for (int j=1;j<=m;j++)
         if (dis[x[j]]!=oo && dis[y[j]]>dis[x[j]]+z[j])
          {
                printf("-1
    ");
                return 0;
                }
        for (int i=1;i<=n;i++) 
             printf("%d ",dis[i]);
        printf("
    ");
        return 0;
     } 
    bellman-ford

    (3)floyed

    #include<bits/stdc++.h>
    using namespace std;
    int n,m,x,y,z;
    const int oo=123456,maxn=1234;
    int dis[maxn][maxn];
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++)
            {
                dis[i][j]=oo*(i!=j);//当i和j相等时dis[i][j]赋值为0; 
            }
        }
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&x,&y,&z);
            dis[x][y]=min(dis[x][y],z);
        }
        for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
        return 0;
    }
    floyed

    6、有向无环图的拓扑排序

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=100000;
    struct Edge
    {
        int x,y,next;
        Edge(int x=0,int y=0,int next=0):x(x),y(y),next(next){}
    }edge[maxn];
    int head[maxn],sumedge,inn[maxn],que[maxn],Head=1,tail=0;
    void add_edge(int x,int y)
    {
        edge[++sumedge]=Edge(x,y,head[x]);
        head[x]=sumedge;
    }
    int n,m,x,y;
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&x,&y);
            add_edge(x,y);
            inn[y]++;
        }
        for(int i=1;i<=n;i++)
        {
            if(inn[i]==0)
            que[++tail]=i;
        }
        for(;Head<=tail;Head++)
        {
            int x=que[Head];
            for(int u=head[x];u;u=edge[u].next)
            {
                inn[edge[u].y]--;
                if(inn[edge[u].y]==0)
                que[++tail]=edge[u].y;
            }
        }
        for(int i=1;i<=tail;i++)
        printf("%d ",que[i]);
        return 0;
    }
    拓扑排序

    7、tarjian求强连通分量

    //tarjian 
    #include <bits/stdc++.h>
    using namespace std;
    
    const int maxn=100000+15;
    struct Edge
    {
        int x,y,next;
        Edge(int x=0,int y=0,int next=0):
            x(x),y(y),next(next) {}
    }edge[maxn];
    int sumedge,head[maxn];
    int n,m;
    int ins(int x,int y)
    {
        edge[++sumedge]=Edge(x,y,head[x]);
        return head[x]=sumedge;
    }
    bool instack[maxn];
    int top,Stack[maxn];
    int dfn[maxn],low[maxn],tim;
    bool vis[maxn];
    int col[maxn],sumcol;
    int dfs(int now)
    {
        dfn[now]=low[now]=++tim;
        Stack[++top]=now;
        vis[now]=true;//入栈并且访问过 
        instack[now]=true;
        for (int u=head[now];u;u=edge[u].next)
         if (instack[edge[u].y])//如果在栈中 说明往回找边了 
          low[now]=min(low[now],dfn[edge[u].y]);//low和dfn求最小值 因为已经在栈中 就不是 now结点子树的结点 就要 low和dfn求最小值 
         else
         if (!vis[edge[u].y])
         {
             dfs(edge[u].y);
             low[now]=min(low[now],low[edge[u].y]);
         }
         else//应经vis过并且不在栈中的 已经给它找到了连通图 不做处理 
         {
         }
        if (low[now]==dfn[now])
        {
            sumcol++;
            col[now]=sumcol;
            while (Stack[top]!=now)
            {
                col[Stack[top]]=sumcol;//将一个连通图上染上一样的颜色 
                instack[Stack[top]]=false;
                top--;
            }
            instack[now]=false;
            top--;
        }
        return 0;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for (int i=1;i<=m;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            ins(x,y);
        }
        for (int i=1;i<=n;i++)
         if (!vis[i]) dfs(i);
        return 0;
    }
    tarjian

    8、tarjian缩点

    //缩点 
    #include <bits/stdc++.h>
    using namespace std;
    
    const int maxn=100000+15;
    struct Edge
    {
        int x,y,next;
        Edge(int x=0,int y=0,int next=0):
            x(x),y(y),next(next) {}
    }edge[maxn],edge2[maxn];
    map <int, bool > Map[maxn];
    int sumedge,head[maxn];
    int n,m;
    int sumedge2,head2[maxn];
    int ins2(int x,int y)
    {
        edge2[++sumedge2]=Edge(x,y,head2[x]);
        return head2[x]=sumedge;
    }
    int ins(int x,int y)
    {
        edge[++sumedge]=Edge(x,y,head[x]);
        return head[x]=sumedge;
    }
    bool instack[maxn];
    int top,Stack[maxn];
    int dfn[maxn],low[maxn],tim;
    bool vis[maxn];
    int col[maxn],sumcol;
    int dfs(int now)
    {
        dfn[now]=low[now]=++tim;
        Stack[++top]=now;
        vis[now]=true;
        instack[now]=true;
        for (int u=head[now];u;u=edge[u].next)
         if (instack[edge[u].y])
          low[now]=min(low[now],dfn[edge[u].y]);
         else
         if (!vis[edge[u].y])
         {
             dfs(edge[u].y);
             low[now]=min(low[now],low[edge[u].y]);
         }
         else
         {
         }
        if (low[now]==dfn[now])
        {
            sumcol++;
            col[now]=sumcol;
            while (Stack[top]!=now)
            {
                col[Stack[top]]=sumcol;
                instack[Stack[top]]=false;
                top--;
            }
            instack[now]=false;
            top--;
        }
        return 0;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for (int i=1;i<=m;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            ins(x,y);
        }
        for (int i=1;i<=n;i++)
         if (!vis[i]) dfs(i);
        for (int i=1;i<=n;i++)
         for (int u=head[i];u;u=edge[u].next)
          if (col[i]!=col[edge[u].y])
           if (Map[col[i]].find(col[edge[u].y])==Map[col[i]].end())
           //相当于 if(map[col[i]][col[edge[u].y]==false)如果这两个不在同一个强连通分量的点没有连起来 为了防止重复加边 所以加一个判断条件
           //如果map[][] 点很多时开的太大 所以用Map[].find ,map<int,bool>Map[maxn]可以理解为bool型的Map[][]; 
           {
               Map[col[i]][col[edge[u].y]]=true;
               ins2(col[i],col[edge[u].y]);
           }
        return 0;
    tarjian缩点

    9、tarjian求割点 割边

    #include <bits/stdc++.h>
    using namespace std;
    
    const int maxn=100000+15;
    struct Edge
    {
        int x,y,next;
        Edge(int x=0,int y=0,int next=0):
            x(x),y(y),next(next) {}
    }edge[maxn*2];
    int sumedge,head[maxn],n,m;
    int ins(int x,int y)
    {
        edge[++sumedge]=Edge(x,y,head[x]);
        return head[x]=sumedge;
    }
    int low[maxn],dfn[maxn],tim;
    bool vis[maxn];
    bool cutedge[maxn],cutpoint[maxn];
    int dfs(int now,int pre)  //需要多记录父边的编号 
    {
        dfn[now]=low[now]=++tim;
        vis[now]=true;
        int sum=0; //树边数目
        bool boo=false; 
        for (int u=head[now];u;u=edge[u].next)
        if ((u^1)!=pre)  //确定不是父边 
         if (!vis[edge[u].y])
          {
              sum++;
              dfs(edge[u].y,u);
              if (low[edge[u].y]>dfn[now])  //判断割边 
              {
                  cutedge[u/2]=true;  //u/2为边的实际编号 
              }
            if (low[edge[u].y]>=dfn[now])  //判断割点 
             boo=true;
              low[now]=min(low[now],low[edge[u].y]);
          }
          else 
          {
              low[now]=min(low[now],dfn[edge[u].y]);
          }
        if (pre==-1)  //分情况判断割点 
        {
            if (sum>1) cutpoint[now]=true;
        }
        else
        {
            if (boo) cutpoint[now]=true;
        }
        return 0;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        sumedge=-1;  //使得代表同一条边的两边编号更有关系 
        memset(head,-1,sizeof(head));
        for (int i=1;i<=m;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            ins(x,y);
            ins(y,x);
        }
        for (int i=1;i<=n;i++)
         if (!vis[i]) dfs(i,-1);
        return 0;
    }
    割点 割边

    10、二分图

    (1)染色

    //O (M+N) 点数+边数 
    #include<bits/stdc++.h>
    using namespace std;
    
    const int maxn=10000+15;
    struct Edge 
    {
        int x,y,next;
        Edge(int x=0,int y=0,int next=0):x(x),y(y),next(next){}
    }edge[maxn];
    int head[maxn],col[maxn],que[maxn],sumedge,x,y,n,m,h,t;
    int add_edge(int x,int y)
    {
        edge[++sumedge]=Edge(x,y,head[x]);
        return head[x]=sumedge;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&x,&y);
            add_edge(x,y);
            add_edge(y,x);
        }
        memset(col,-1,sizeof(col));
        que[h=t=1]=1;
        col[1]=0;
        for(;h<=t;h++)
        {
            int x=que[h];
            for(int u=head[x];u;u=edge[u].next)
            if(col[edge[u].y]!=-1)//已经染上色 
            {
                if(col[edge[u].y]==col[x])//边的两边染了相同的颜色 
                {
                    printf("Wrong!
    ");
                    return 0;
                }
            }
            else
            {
                col[edge[u].y]=col[x]^1;
                que[++t]=edge[u].y;
            }
        }
        return 0;
    }
    染色

    (2)并查集

    #include<bits/stdc++.h>
    using namespace std;
    
    const int maxn=1000+15;
    int far[maxn],n,m,x,y;
    int f(int x)
    {
        return far[x]==x?x:far[x]=f(far[x]);
     } 
     int main()
     {
         scanf("%d%d",&n,&m);
         for(int i=1;i<=m;i++)
         {
             scanf("%d%d",&x,&y);
             int fx1=f(x*2),fy1=f(y*2-1);//一个染黑一个染白 
             far[fx1]=fy1;
             int fx2=f(x*2-1),fy2=f(y*2);//一个染白一个染黑 
             far[fx2]=fy2;
         }
         for(int i=1;i<=n;i++)
         {
             if(f(i*2)==f(i*2-1)) //如果这个点既染黑又染白 
             {
                 printf("Wrong!");
                 return 0;
             }
         }
     }
    并查集

    //呼~终于整理完了
    //这个学长好温柔啊QWQ

  • 相关阅读:
    二分排序之三行代码
    最短路径—Floyd算法
    最小生成树 Kruskal算法
    最小生成树-Prim算法
    最短路径之迪杰斯特拉(Dijkstra)算法
    C函数指针
    Linux进程的实际用户ID和有效用户ID
    C++ 之Boost 实用工具类及简单使用
    ELK之消息队列选择redis_kafka_rabbitmq
    Python文件操作
  • 原文地址:https://www.cnblogs.com/zzyh/p/6790428.html
Copyright © 2011-2022 走看看