说明
在许多图论的题中,存储图是必不可少的,这里就来说说我平时存储图的方式,与大家交流交流 罒V罒
容器
# | 说明 |
---|---|
num | 记录到现在已经存了多少条边了(在添加边的时候会用到) |
head[x] | 以 x 为入点的某一条边的编号 |
.to | 第 x 条边的下一条边的编号(可以把它想象为一个通往下一个边的链接) |
.v | 这一条边的出点(入点当然还是开始的那个head[x] 中的x) |
.w | 边权值(不需要的话可以不要,根据题目也可以再加几个参数) |
int head[N],num;
struct edge{
int to,v,w;
} e[N];
添加边
- 看看程序和注释,感受一下吧
void add(int u,int v,int w){
num++; //新的一条边
e[num].v=v; //处理一下这条边的信息
e[num].w=w;
e[num].to=head[u]; //把 当前 head[u] 那条边接到 num 后面
head[u]=num; //把这条边接到 u 的 head[] 上
}
枚举边
- 当我们我们需要枚举所有以某个点(这里就当成是
x
吧)为入点的边,就可以用到下面的程序 - 由于起初
e[].to
都是 0 (当然我是这样的,你们喜欢也可以初始化成-1),所以就用e[].to
是否为0来判断判断是否还有边。 - 这个代码是为了理解才这么长,最后提示中有个不错的宏定义,平时就用那个,又快又方便!
for (int i=head[x],o; i!=0; i=e[i].to){
o=e[i].v;
//这里就可以做各种处理操作
printf("%d -> %d (%d)\n",x,o,e[i].w);
}
遍历图
- 要是是个图,那么就用个数组
f[]
来记录某个点是否已经讨论过了(有时按题目需要,可能是用时间戳,不过也差不多……) - 代码被我简化了,
For(x)
意思就是上面那个枚举边,o
就是枚举出的出点。
void dfs(int x){
f[o]=1;
For(x) if (!f[o]){
//这里可以做各种操作
dfs(o);
}
}
- 要是目标是个树(特殊的图,没有环出现),那么我们就可以省掉
f[]
的空间,每次传个当前节点的父节点fa
就行了。 - 原理大概就是……
可以自己画个图,感受一下
void dfs(int x,int fa) {
For(x) if (o!=fa){
//这里可以做各种操作
dfs(o,x) ;
}
}
提示
- 有的比赛(比如 codeforces)中,不仅要求你做出题目,完成的速度也是评分的一个要素,那么你就需要好好利用宏定义来简化代码,这样对别人看懂你的代码这方面也有很大帮助。
- 比如你就可以定义一个
For
,让程序更简洁
#define For(x) for (int h=head[x],o=e[h].v; h; o=e[h=e[h].to].v)
- 还有一点很重要,对于无向边,每次要两边都加一次边(即出入点调换一次再加一次),这样一来,我们存边的数组就得开两倍空间了。
- 这里有几个用到邻接表的题目,可以看看,实践一下。
- http://blog.csdn.net/jackypigpig/article/details/69360897
- http://blog.csdn.net/jackypigpig/article/details/69808594
- http://blog.csdn.net/jackypigpig/article/details/70227554