最短路,最小生成树,及拓扑排序模板整理

最短路,最小生成树,及拓扑排序都是图论基础算法,这里不再赘述,只进行模板整理,如有疑问还请评论留言
对于最短路算法,这里除常用的SPFA和堆优化Dijkstra外,还整理了许多学校和算法竞赛培训机构不会教授的Bellman_ford与原版Dijkstra,以便于读者理解前面的两种优化版本。

最短路:

最短路算法经典例题
http://codevs.cn/problem/1557/热浪

//Bellman_ford版本
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
const int INF=100000000; 
using namespace std;
int t,c,ts,te,cnt;
int u[100000],v[100000],d[100000],w[100000];
void Bellman_ford()
{
    for(int i=1;i<=t;i++)
    d[i]=INF;
    d[ts]=0;
    for(int i=1;i<=t-1;i++)
    for(int j=1;j<=cnt;j++)
    {
        if(d[u[j]]<INF)
        d[v[j]]=min(d[v[j]],d[u[j]]+w[j]);
    }
}
int main()
{
    scanf("%d%d%d%d",&t,&c,&ts,&te);
    cnt=c;
    for(int i=1;i<=c;i++)
    {
        scanf("%d%d%d",&u[i],&v[i],&w[i]);
        u[++cnt]=v[i];
        v[cnt]=u[i];
        w[cnt]=w[i];
    }
    Bellman_ford();
    printf("%d",d[te]);
    return 0;
}



//SPFA邻接链表版本
//SPFA是Bellman_ford的队列优化
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
const int INF=1000000000;
int t,c,ts,te,ru,rv,rw,tot,x;
int d[100000],first[100000],next[100000];
bool inq[100000];
queue<int>q;
struct Edge
{
    int u,v,w;
}l[100000];
void build(int u,int v,int w)
{
    l[++tot]=(Edge){u,v,w};
    next[tot]=first[u];
    first[u]=tot;
}
void SPFA()
{
    for(int i=1;i<=t;i++)
    d[i]=INF;
    d[ts]=0;
    inq[ts]=1;
    q.push(ts);
    while(!q.empty())
    {
        int k=q.front();
        q.pop();
        inq[k]=0;
        for(int i=first[k];i!=-1;i=next[i])
        {
            x=l[i].v;
            if(d[k]<INF&&d[x]>d[k]+l[i].w)
            {
                d[x]=d[k]+l[i].w;
                if(!inq[x])
                q.push(x);
                inq[x]=1;
            }
        }
    }
}
int main()
{
    memset(first,-1,sizeof(first));
    scanf("%d%d%d%d",&t,&c,&ts,&te);
    for(int i=1;i<=c;i++)
    {
        scanf("%d%d%d",&ru,&rv,&rw);
        build(ru,rv,rw);
        build(rv,ru,rw);
    }
    SPFA();
    printf("%d",d[te]);
    return 0;
}



//SPFA邻接表版本
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
const int INF=1000000000;
int t,c,ts,te,ru,rv,rw,tot,x,w;
int d[100000];
bool inq[100000];
vector<int>G[100000];
vector<int>V[100000];
queue<int>q;
inline void build(int u,int v,int w)
{
     G[u].push_back(v);
     V[u].push_back(w);
     G[v].push_back(u);
     V[v].push_back(w);
}
void SPFA()
{
    for(int i=1;i<=t;i++)
    d[i]=INF;
    d[ts]=0;
    inq[ts]=1;
    q.push(ts);
    while(!q.empty())
    {
        int k=q.front();
        q.pop();
        inq[k]=0;
        for(int i=0;i<G[k].size();i++)
        {
            x=G[k][i];
            w=V[k][i];
            if(d[k]<INF&&d[x]>d[k]+w)
            {
                d[x]=d[k]+w;
                if(!inq[x])
                q.push(x);
                inq[x]=1;
            }
        }
    }
}
int main()
{
    scanf("%d%d%d%d",&t,&c,&ts,&te);
    for(int i=1;i<=c;i++)
    {
        scanf("%d%d%d",&ru,&rv,&rw);
        build(ru,rv,rw);
    }
    SPFA();
    printf("%d",d[te]);
    return 0;
}



//Dijkstra版本
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
const int INF=1000000000;
int t,c,ts,te,x,y,r;
int d[1000001];
int w[2500][2500]; 
bool v[1000001];
void dijkstra()
{
    for(int i=1;i<=t;i++)
    d[i]=INF;
    d[ts]=0;
    for(int i=1;i<=t;i++)
    {
        int k=0,m=INF;
        for(int j=1;j<=t;j++)
        {
            if(!v[j]&&d[j]<=m)
            {
                m=d[j];
                k=j;
            }
        }
        v[k]=1;
        for(int j=1;j<=t;j++)
        if(!v[j])
        d[j]=min(d[j],d[k]+w[k][j]);
    }
}
int main()
{
    scanf("%d%d%d%d",&t,&c,&ts,&te);
    for(int i=1;i<=t;i++)
    for(int j=1;j<=t;j++)
    {
        if(i!=j)
        w[i][j]=INF;
    }
    for(int i=1;i<=c;i++)
    {
        scanf("%d%d%d",&x,&y,&r);
        w[x][y]=w[y][x]=r;
    }
    dijkstra();
    printf("%d",d[te]);
    return 0;
}



//Dijkstra,堆优化,邻接链表版本
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int MAXN = 100000 + 5;
int t,c,ts,te,tot,rs,re,rc;
int dis[MAXN],first[MAXN],next[MAXN];
bool done[MAXN];
struct edge
{
    int f,t,v;
}l[MAXN << 1];
struct zt
{
    int num;
    int d;//距离用于排序 
    /*bool operator < (zt a)const
    {
        return d>a.d;   
    }
    friend bool operator < (zt a,zt b)
    {
        return a.d<b.d;
    }*///2种写法,仅对结构体里的小于号生效
}p[MAXN];
priority_queue<zt>q;
bool operator < (zt a,zt b)
{
    return a.d>b.d;
}
void build(int f,int t,int v)
{
    l[++tot]=(edge){f,t,v};
    next[tot]=first[f];
    first[f]=tot;
}
void dijkstra()
{
    while(!q.empty()) q.pop();
    q.push((zt){ts,0});
    dis[ts]=0;
    while(!q.empty())
    {
        zt a=q.top();
        int u=a.num;
        q.pop();
        if(done[u]==1)
        continue;
        done[u]=1;
        for(int i=first[u];i!=-1;i=next[i])
        {
            int v=l[i].t;
            if(dis[v]>dis[u]+l[i].v)
            {
                dis[v]=dis[u]+l[i].v;
                q.push((zt){v,dis[v]});
            }
        }
    }
}
int main()
{
    memset(dis,0x3f,sizeof(dis));
    memset(first,-1,sizeof(first));
    scanf("%d%d%d%d",&t,&c,&ts,&te);
    for(int i=1;i<=c;i++)
    {
        scanf("%d%d%d",&rs,&re,&rc);
        build(rs,re,rc);
        build(re,rs,rc);
    }
    dijkstra();
    printf("%d",dis[te]);
    return 0;
}



http://codevs.cn/problem/1021/玛丽卡
注意题目描述中“最糟糕的情况”指的是玛丽卡。
对答案有贡献的路径只存在于最短路上
先跑一边SPFA找出最短路上的边,依次尝试删除每条边,跑SPFA统计答案即可

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
const int INF = 1000000007;
int n,m,ru,rv,rw,tot,ans;
int dis[500010],first[1000010],nxt[1000010],pre[500010];
bool flag;
bool inq[100010];
struct edge
{
    int u,v,w;
}l[1000010];
queue<int>q;
void build(int f,int t,int c)
{
    l[++tot]=(edge){f,t,c};
    nxt[tot]=first[f];
    first[f]=tot;
}
void SPFA(int l1,int l2)
{
    for(int i=1;i<=n;i++)
    dis[i]=INF;
    dis[1]=0;
    q.push(1);
    inq[1]=1;
    while(!q.empty())
    {
        int k=q.front();
        q.pop();
        inq[k]=0;
        for(int i=first[k];i!=-1;i=nxt[i])
        {
            int x=l[i].v;
            if((k==l1&&x==l2)||(k==l2&&x==l1))
            continue;
            if(dis[x]>dis[k]+l[i].w)
            {
                dis[x]=dis[k]+l[i].w;
                if(!flag)
                pre[x]=k;//记录最短路径上的边
                if(!inq[x])//若每次更新都记录起点及起点出发的边的编号会导致记录部分不在最短路径上的边...虽然不影响答案
                {
                    q.push(x);
                    inq[x]=1;
                }
            }
        }
    }
}
int main()
{
    memset(first,-1,sizeof(first));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&ru,&rv,&rw);
        build(ru,rv,rw);
        build(rv,ru,rw);
    }
    tot=0;
    SPFA(0,0);
    flag=1;
    for(int i=n;i;i=pre[i])
    {
        SPFA(i,pre[i]);
        ans=max(ans,dis[n]);
    }
    printf("%d",ans);
    return 0;
}


最小生成树:

http://codevs.cn/problem/1231/最优布线问题

//Kruskal版本
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
typedef unsigned long long LL;
int n,m,cnt,x,y,e;
int u[1000000],v[1000000],w[1000000],r[1000000],fa[1000000];
LL cost;
bool cmp(int a,int b)
{
    return w[a]<w[b];
}
int find(int x)
{
    return fa[x]==x?x:fa[x]=find(fa[x]);
}
void kruskal()
{
    for(int i=1;i<=n;i++)
    fa[i]=i;
    for(int i=1;i<=m;i++)
    r[i]=i;
    sort(r+1,r+m+1,cmp);
    for(int i=1;i<=m;i++)
    {
        e=r[i];
        x=find(u[e]);
        y=find(v[e]);
        if(x!=y)
        {
            cost+=w[e];
            fa[x]=y;
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    cnt=n;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&u[i],&v[i],&w[i]);
    }
    kruskal();
    printf("%lld",cost);
    return 0;
}



//Prim
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
typedef unsigned long long LL;
const int MAXN=1000000;
LL n,m,rf,rt,rv,tot,cost,cnt;
LL first[MAXN],next[MAXN];
bool done[MAXN];
struct Edge
{
    LL f,t,v;
}l[MAXN];
struct condition 
{
    LL num,d;
}p[MAXN];
priority_queue<condition>q;
bool operator < (condition a,condition b)
{
    return a.d>b.d;
}
void build(int f,int t,int v)
{
    l[++tot]=(Edge){f,t,v};
    next[tot]=first[f];
    first[f]=tot;
}
void prim()
{
    while(!q.empty()) q.pop();
    q.push((condition){1,0});
    while(!q.empty()&&cnt<n)
    {
        condition a=q.top();
        LL u=a.num;
        LL w=a.d;
        q.pop();
        if(done[u])
        continue;
        done[u]=1;
        cost+=w;
        cnt++;
        for(int i=first[u];i!=-1;i=next[i])
        if(!done[l[i].t])
        q.push((condition){l[i].t,l[i].v});
    }
}
int main()
{
    memset(first,-1,sizeof(first));
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%lld%lld%lld",&rf,&rt,&rv);
        build(rf,rt,rv);
        build(rt,rf,rv);
    }
    prim();
    printf("%lld",cost);
    return 0;
}


拓扑排序:

http://codevs.cn/problem/2833/奇怪的梦境

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
queue<int>q;
int n,m,ru,rv,tot,cnt,fla;
int first[100001],next[100001],pas[100001],ans[100001],jud1[100001],jud2[100001];
bool flag;
struct spot
{
    int u,v;
}l[100001];
void build(int f,int t)
{
    l[++tot]=(spot){f,t};
    next[tot]=first[f];
    first[f]=tot;
}
int main()
{
    memset(first,-1,sizeof(first));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&ru,&rv);
        if(jud1[ru]==rv)
        continue;
        pas[rv]++;
        jud1[ru]=rv;
        build(ru,rv);
    }
    for(int i=1;i<=n;i++)
    if(pas[i]==0)
    q.push(i);
    while(!q.empty())
    {
        int x=q.front();
        q.pop();
        ans[++cnt]=x;
        for(int i=first[x];i!=-1;i=next[i])
        {
            pas[l[i].v]--;
            if(pas[l[i].v]==0)
            q.push(l[i].v);
        }
    }
    if(cnt>=n)
    printf("o(∩_∩)o");
    else
    {
        printf("T_T\n");
        printf("%d",n-cnt);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值