4.29--4.30 图论

本文总结了图论中的经典算法,包括二叉堆操作、最小生成树(Prim算法、Kruskal算法)、最短路径(Dijkstra算法、Bellman-Ford算法、Floyd算法)、有向无环图拓扑排序、Tarjan算法及其应用(强连通分量、割点割边)、二分图染色等,提供了详细的实现代码。
摘要由CSDN通过智能技术生成

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\n",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\n",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\n");
            return 0;
            }
    for (int i=1;i<=n;i++) 
         printf("%d ",dis[i]);
    printf("\n");
    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!\n");
                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

 

转载于:https://www.cnblogs.com/zzyh/p/6790428.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值