NOIP图论总结

本文详细总结了NOIP竞赛中的图论知识,包括图的存储(前向星、邻接矩阵、邻接表)、最短路算法(SPFA、Dijkstra、Floyd)、最小生成树(Kruscal)、强连通分量(Tarjan)、二分图染色及最大匹配、其他图论算法(FloodFill、EulerPath、TopoSort)以及相关例题解析,是OI图论学习的重要参考资料。
摘要由CSDN通过智能技术生成

1.图的存储

前向星

struct Edge{
    int to,dist,next;
}edges[MAXM];//边
int head[MAXN],cnt=0;
//加边
void addedge(int from,int to,int dist){
    edges[cnt].to=to;
    edges[cnt].dist=dist;
    edges[cnt].next=head[from];
    head[from]=cnt++;
}

注意:无向图时应调用两遍addedge
上述的head初始值为-1因此遍历以u为from的所有边的时候可以采用下面的代码

for(int i=head[u];~i;i=edges[i].next){
    //do something
}

邻接矩阵

一般跑Floyd的时候这么存。设G[i][j]表示i到j有一条边边权为G[i][j]否则G[i][j]=INF。

邻接表(vector)

刘汝佳推荐的存法,用一个数组edges存边集,G[i][j]存以i为起点的第j条边的编号。

struct Edge{
    int from,to,dist;
    Edge(int f=0,int t=0,int d=0){
        from=f;to=t;dist=d;
    }
};
vector<Edge> edges;
vector<int> G[MAXN];

void addedge(int from,int to,int dist){
    edges.push_back(Edge(from,to,dist));
    int m=edges.size();
    G[from].push_back(m-1);
}//加边

//遍历以u为起点的所有边
void work(int u){
    for(int i=0;i<G[u].size();i++){
        //do something
    }
}

2.最短路

单源最短路

一般来说单源最短路最常用的就是以下两种算法
以下存图方式均为前向星

SPFA

一般来说有负权环时用SFPA。
Code:

queue<int> q;
int d[MAXN];
int cnt[MAXN];
int inq[MAXN];

int SFPA(int S,int T){
    memset(inq,0,sizeof(inq));
    memset(cnt,0,sizeof(cnt));
    for(int i=1;i<=n;i++) d[i]=INF;
    d[S]=0;
    q.push(S);inq[S]=1;cnt[S]++;
    while(!q.empty()){
        int u=q.front();q.pop();inq[u]=0;
        for(int i=head[u];~i;i=edges[i].next){
            int v=edges[i].to;
            if(d[v]>d[u]+edges[i].dist){
                d[v]=d[u]+edges[i].dist;
                if(!inq[v]){
                    q.push(v);inq[v]=1;
                    if(++cnt[v]>n) return -1;//存在负权环
                }
            }
        }
    }
}

Dijkstra

一般来说稀疏图用Dijkstra。
讲道理一般不用Floyd的都用Dijkstra。
Code:

struct Node{
    int d,u;
    bool operator < (const Node &rhs) const{
        return d>rhs.d;
    }
}Hp[MAXM];
int sz=0;

int d[MAXN];
bool done[MAXN];

int Dijsktra(int S,int T){
    memset(done,false,sizeof(done));
    for(int i=1;i<=n;i++) d[i]=INF;
    d[S]=0;
    Hp[sz++]=(Node){
  0,S};
    while(sz){
        pop_heap(Hp,Hp+sz);sz--;
        int u=Hp[sz].u;
        if(done[u]) continue;
        done[u]=true;
        for(int i=head[u];~i;i=edges[i].nxt)if(!vis[i]){
            Edge &e=edges[i];
            if(d[e.to]>d[u]+e.dist){
  //松弛
                pre[e.to]=i;//记录路径
                d[e.to]=d[u]+e.dist;
                Hp[sz++]=(Node){d[e.to],e.to};
                push_heap(Hp,Hp+sz);
            }
        }
    }
    return d[T];
}

最短路径的输出

存储:定义一个pre数组然后在松弛操作处进行更新即可。具体代码见Dijkstra。
输出:倒序输出,从to开始一直往前找,直到找到from为止

void printpath(int from,int to){
    int u=to;
    while(from!=u){
        u=edges[pre[u]].from;
        printf("from:%d to:%d\n",u,edges[pre[u]].to);
    }
}

注意这里输出的路径是倒序的,如果要正序输出,将路径暂时存储到栈中即可。

全源最短路

Dijkstra SPFA

如果图是稀疏图的话,对每个节点跑一边单源最短路即可得到全源最短路。

Floyd

三重for循环

void floyd(){
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++) 
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值