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++)