图论的模板

1.邻接表

//定义邻接表
struct edge {int to,Next,v;}e[Maxm];
int h[Maxn];

//建边
void Addedge(int x,int y,int z)
{
    e[++cnt].to=y;
    e[cnt].Next=h[x];
    e[cnt].v=z;
    h[x]=cnt;
}

//遍历
for(int i=h[x];i;i=e[i].Next)
{
    int y=e[i].to
    ......
}

2.拓扑排序

(1)邻接矩阵法

bool tpsort()
{
    for(int i=1;i<=n;i++)
    {
        int j=1;
        while(j<=n&&rd[j]) j++; //找入度为0的点
        if(j>n) return 0; //没找到则不存在拓扑序列
        ans[++ans[0]]=j; //加入序列
        rd[j]=-1; //使其入度不再为0(相当于打一个标记)
        for(int k=1;k<=n;k++) if(e[j][k]) rd[k]--; //断边
    }
    return 1;
}

(2)邻接表法

bool topsort()
{
    int top=0;
    for(int i=1;i<=n;i++)
        if(rd[i]==0) ans[++ans[0]]=i; //将入度为0的点加入序列
    while(top<ans[0])
    {
        int j=ans[++top]; //选取当前入度为0的点
        for(int i=h[x];i;i=e[i].Next)
        {
            int y=e[i].to;
            rd[y]--; 
            if(rd[y]==0) ans[++ans[0]]=y; //如果入度为0则加入序列
        }
    }
    if(top==n) return 1;
    return 0;
}

(3)邻接表+堆

bool topsort()
{
    priority_queue<int,vector<int>,greater<int> >pq;
    for(int i=1;i<=n;i++) if(rd[i]==0) pq.push(i);
    while(!pq.empty())
    {
        int x=pq.top(); pq.pop(); //小根堆维护 
        ans[++ans[0]]=x;
        for(int i=h[x];i;i=e[i].Next)
        {
            int y=e[i].to;
            rd[y]--;
            if(rd[y]==0) pq.push(y);
        }
    }
    if(ans[0]==n) return 1;
    return 0;
}

3.最小生成树

(1)Prim算法

int prim(int x)
{
    int ans=0;
    for(int i=1;i<=n;i++) d[i]=INF;
    d[x]=0;
    for(int i=1;i<=n;i++)
    {
        int minn=INF,k;
        for(int j=1;j<=n;j++)
            if(!flag[j] && minn>d[j]) {minn=d[j]; k=j;}
        flag[k]=1;
        ans+=d[k];
        for(int j=1;j<=n;j++)
            if(!flag[j] && d[j]>e[k][j]) d[j]=e[k][j];
    }
    return ans;
}

(2)Kruskal

struct edge {int y,v,w}a[Maxm];
//排序的时候就可以按照a[i].w来排
void kruskal()
{
    int k=0;
    for(int i=1;i<=m;i++)
    {
        int f1=getfather(a[i].u);
        int f2=getfather(a[i].v);
        if(f1==f2) continue;
        ans+=a[i].w;
        f[f1]=f2;
        k++;
        if(k>n) break;
    }
}

4.最短路径

(1)Dijkstra算法

int dijkstra(int s,int t)
{
    for(int i=1;i<=n;i++) d[i]=INF;
    d[s]=0;
    for(int i=1;i<=n;i++)
    {
        int minn=INF,k;
        for(int j=1;j<=n;j++)
            if(!flag[j] && minn>d[j]){minn=d[j]; k=j;}
        flag[k]=1;
        for(int j=1;j<=n;j++)
            if(e[k][j]&&!flag[j]&&d[k]+e[k][j]<d[j])
                d[j]=d[k]+e[k][j];
    }
    return d[t];
}

(2)Floyd算法

void floyd()
{ 
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if(e[i][k]!=INF&&e[k][j]!=INF&&e[i][k]+e[k][j]<e[i][j])
                    e[i][j]=e[i][k]+e[k][j];
}

(3)SPFA算法

int spfa(int s,int t)
{
    queue<int>q;
    for(int i=1;i<=n;i++) d[i]=INF;
    d[s]=0; q.push(s);
    while(!q.empty())
    {
        int x=q.front(); q.pop();
        flag[x]=0;
        for(int i=h[x];i;i=e[i].Next)
        {
            int y=e[i].to;
            if(d[y]>d[x]+e[i].v)
            {
                d[y]=d[x]+e[i].v;
                if(!flag[y]) {flag[y]=1; q.push(y);}
            }
        }
    }
    return d[t];
}

5.图的连通性

5.1强联通分量

(1)Tarjan算法

void tarjan(int x)
{
    dfn[x]=low[x]=++sign;
    s[++top]=x;
    flag[x]=1;
    for(int i=1;i<=n;i++)
        if(e[x][i])
        {
            if(dfn[i]==0)
            {
                tarjan(i);
                low[x]=min(low[x],low[i]);
            }
            else if(flag[i]) low[x]=min(low[x],dfn[i]);
        }
    if(low[x]==dfn[x])
    {
        num++;
        int y;
        do
        {
            y=s[top--];
            flag[y]=0;
        }
        while(y!=x);
    }
}

(2)缩点

//加belong数组(记录y点属于哪个强联通分量)
if(low[x]==dfn[x])
{
    num++;
    int y;
    do
    {
        y=s[top--];
        flag[y]=0;
        belong[y]=num;
    }
    while(y!=x);
}

//计算缩点后的入度出度
for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    {
        if(e[j][i]&&belong[i]!=belong[j]) rd[belong[i]]++;
        if(e[i][j]&&belong[i]!=belong[j]) cd[belong[i]]++;
    }

5.2割点

(1)Tarjan算法

void dfs(int x)
{
    dfn[x]=low[x]=++sign;
    for(int i=1;i<=n;i++)
        if(e[x][i]&&prt[x]!=i)
        {
            if(dfn[i]==0)
            {
                prt[i]=x;
                dfs(i);
                low[x]=min(low[x],low[i]);
                if(low[i]>=dfn[x])
                {
                    if(x==1) // 特判起点节点的情况
                    {
                        son++;
                        if(son==2) ans++;
                    }
                    else ans++;
                }
            }
            else low[x]=min(low[x],dfn[i]);
        }
}

5.3割边

void dfs(int x)
{
    dfn[x]=low[x]=++sign;
    for(int i=1;i<=n;i++)
        if(e[x][i]&&prt[x]!=i)
        {
            if(dfn[i]==0)
            {
                prt[i]=x;
                dfs(i);
                low[x]=min(low[x],low[i]);
                if(low[i]>dfn[x]) ans++;
            }
            else low[x]=min(low[x],dfn[i]);
        }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值