之前在学习最大流的时候,EK算法使用的是一个二维数组,但是对于稀疏图,二维数组比较浪费空间,对于稠密图,EK算法又解决不了,使用Dinic的时候需要一种比较好的存图方法,下面就介绍一下存图利器——链式前向星
对于存图的各种数据结构,复杂度如下
邻接矩阵
O(1)(查询一条边) O(n)枚举出边 O(N*N)空间复杂度
前向星
O(n)(查询一条边) O(n)枚举出边 O(n)空间复杂度 (预处理时
间复杂度(O(NlgN)))
链式前向星
O(n)(查询一条边) O(n)枚举出边 O(n)空间复杂度
我们发现对于与一道题若n逼近10^6时,前两者的时间就不行了,而链式前向星还可以支撑这种数据规模
链式前向星的原理
其中edge[i].to
表示第 i 条边的终点,edge[i].next
更像一个指针,表示与第 i 条变同起点的下一条边的存储位置,edge[i].w
为权值
head[] 数组一般初始化为-1,表示与结点u有连边的第一个点的地址
void add(int u,int v,int w)
{
edge[cnt].w = w;
edge[cnt].to = v;
edge[cnt].next = head[u];//存的是以u为起点边的下一条边
head[u] = cnt++;//存的是以u为起点的最后一条输入的边的序号cnt
//比如以1为起点的最后一条边的序号cnt=5,即第五条输入的边
}
初始化 cnt = 0
,按照上面的图模拟输入
我们需要知道一点,在链式前向星中,从上到下是存储的书讯,遍历的时候需要从下到上!
我们只看以 1 为起点的存边情况(蓝色直线)
首先以 1 为起点的边 0 的下一条边为 -1 ,说明其为最后一条边;第二次储存时,以 1 为起点的边 3 的下一条边时 0 ,head储存最新的边序号为 3 ;第三次储存时,以 1 为起点的边 5 的下一条边序号为3 ,head 储存最新的边 5。
总结
head[i] 保存的是以 i 为起点的所有边中最大边的序号,把这条边当做顶点i的第一条边的起始位置,所以这样在遍历的时候就是倒着的,与输入的顺序相反,不影响结果的正确性。
代码实现
struct Edge{
int next,to,w;//下一条边的储存下标,这条边的终点,权值
};
Edge edge[500010];
void Add(int u, int v, int w) { //增边
edge[++cnt].next = head[u];//cnt为计数,从1开始
edge[cnt].to = v;
edge[cnt].w = w;
head[u] = cnt;//第一条边为当前边
}
for(int i=head[s]; i!=0; i=edge[i].next) //遍历
{
}
例题 洛谷3371
#include<iostream>
#include<queue>
#include<vector>
using namespace std;
const long long inf = 2147483647;
const int maxn = 500005;
int m,n,s,t,cnt=0;
int dis[maxn],vis[maxn],head[maxn];
struct edge
{
int next,to,distance;
}edge[maxn];
void addedge(int a,int b,int c)
{
edge[++cnt].next = head[a];
edge[cnt].to = b;
edge[cnt].distance = c;
head[a] = cnt;
}
void fff()
{
queue<int> q;
for(int i=1;i<=n;i++)
{
dis[i] = inf;
vis[i] = 0;
}
q.push(s);
dis[s] = 0;
vis[s] = 1;
while (!q.empty())
{
int u = q.front();q.pop();
vis[u] = 0;
for(int i=head[u];i;i=edge[i].next)
{
int v = edge[i].to;
if(dis[v]>dis[u]+edge[i].distance)
{
dis[v] = dis[u]+edge[i].distance;
if(vis[v]==0)
{
vis[v] = 1;
q.push(v);
}
}
}
}
}
int main()
{
cin>>n>>m>>s;
for(int i=1;i<=m;i++)
{
int a,b,c;
cin>>a>>b>>c;
addedge(a,b,c);
}
fff();
for(int i=1;i<=n;i++)
{
if(s==i) cout<<0<<' ';
else cout<<dis[i]<<' ';
}
return 0;
}
参考 https://blog.csdn.net/Spidy_harker/article/details/88877072