写在前面:凸轮好说歹说也写了这么长时间的代码,但是好长时间都没有认真停下来思考过一些东西,这个总结算是我对凸轮的理解了。
概念神马的先不写;
按照jzyz的教材的顺序来,首先是存储:
一、存储:
(1) 邻接矩阵:这个很好理解了吧,,,如果我定义一个二维数组map[i][j];那么其数组下标就表示从i到j有一条路,权值为存储的值;
(2) 重点说一下邻接表:
邻接表这个小妖精,是需要时间慢慢领悟的:首先,我们用一个link[n]数组来记录所有边的编号,而link[i]中的数组下标表示的也就是起点的编号,,注意是起点,注意是起点,注意是起点;;而link[i]存储的则是从这个点出发的最后一条边的编号,
然后我们再使用一个结构体定义的e数组,结构体的下标是一条边,e用来记录这一条边所对应的下一个点的节点,以及这条路径的权值还有它指的下一条边的编号于是就有下面几行代码
struct edge
{
int y,v,next;
}e[maxn+10];
int link[maxn];
int len=0;
那么这里我为什么要用一个len呢?主要就在于读取数据时的记录了,当我们读入一条边的时候,这条边往往有三个参数:起点,终点,权值,那么在存储的时候,由于我们link数组里面存的是最后一条边的编号,也就是说link数组是在不断动态更新的,但由于邻接表是倒着存储的,所以link数组最后的状态是出来的第一条边
我们在插入一条边的时候,可以先把这个点指向的下一条边更改为++len,再把link数组里的边值改为最新的一条,也就是把原来没有指向的那条边存储为next节点,这时候我们就完成了一条边的加入,因为这条边的基本元素已经被我们存储完了,也就是它的编号,下个点的节点,路径长,下条边的编号,那么这时我们的存储大业也就完成了,伪代码如下:
Void insert(int xx,int yy int vv)
{
e[++len].next=link[xx];link[xx]=len;//完成更新
e[len].y=yy;e[len].v=vv;
}
(3) 至于边表这种东西,它只是用结构体单纯的记录,也就是酱紫的:
struct edge
{
Int x,y,v;
}e[maxn+10];
读入的时候就是酱紫
void init
{
int m;
For(int i=1;i<=n;i++)
{
int xx,yy,vv;
Cin>>xx>>yy>>vv;
E[i].x=xx;
E[i].y=yy;
E[i].v=vv;
}
}
根据教材的顺序,我们就开始图的遍历
二、遍历
首先是dfs
Dfs也就是经典的递归调用,步骤就是这样的:
1、 从出发点开始访问,并且每走过一个记录一个,防止陷入死循环,
2、 回溯,回到上一个节点,继续往底下找,如果是邻接表,那么就有当i的所有临界点都被访问完的时候,也就是下一条边的值为零的时刻,那么就返回上层继续深搜,
伪代码如下:
邻接矩阵:
void dfs(int k)
{
for(int i=1;i<=n;i++)
{
if(map[k][i]&&(!f[i]))
{
f[i]=1;
dfs[i];
}
}
}
邻接表:
void dfs(int k)
{
for(int i=link[k];i;i=e[i].next)
{
if(!f[e[i].y])
{
f[e[i].y]=1;
dfs(e[i].y);
}
}
}
然后是bfs:思路和正常的bfs一样,我用代码注释来解释每一步是什么意思吧
void bfs(int i)
{
memset(q,0,sizeof(q));
int head=0,tail=1;
q[1]=i;f[i]=1;//让起点入队
while(head<tail)
{
k=q[++head];//队尾向前一个单位
for(int j=1;j<=n;j++)//每个点都扫一遍
if(map[k][j]&&!f[j])//如果入队的点没有被扫描
{
q[++tail]=j;//入队
f[j]=1;
}
}
}
三、Euler路???
至今,我都不知道Euler路该怎么写。。。
看起来欧拉路也就是把所有的边都不重复的遍历一遍。
定理(请使用显然法证明):对于一个无向图,如果它每个点的度都是偶数,那么它存在一条欧拉路,如果有且仅有两个点的度为奇数,那么它存在一条欧拉路;如果超过两个奇点,那么就不存在欧拉路了;;;
那么在做欧拉路的题的时候,首先根据上述定理判断存在不存在欧拉路,如果存在,那么我们首先就要考虑起点在哪,那么就有jzyz教材的话(显然证明法是万能的):如果所有点的度都是奇数,就没有欧拉路了,如果有两个奇点,那么起点就是这两个点中的一个,那么另一个就是终点,,,
Ps:然而我还是不会写
四、图的最短路径
根据jzyz教材,我至今为止一共学习了四种最短路的方法,其中bellman-ford一直没用过,一直用spfa在写,所以对bellman-Ford不做讨论;
1、Floyd算法
这是一个很神奇的只有三行的代码,在总结这个算法前,有个很通俗易懂的封闭穿包需要明白,就是如果i到j不能联通,但是i到k联通,k到j联通,那么i到j就是联通的;
明白这个以后,其实Floyd算法就出来了,首先我们先找一个k节点,也就是i到j的中可以途径的第三个节点,如果从i到j的路径比从k转折一下的路径长,那么邻接矩阵的最小值就更新为最小值,代码就是酱紫:
void floyed()
{
for(int k=1;k<=n;k++)//notice here!!!
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
map[i][j]=min(map[i][k]+map[k][j],map[i][j]);
}
注意k在最外层,注意k在最外层,注意k在最外层
*************************分割线**********************
感谢帅气无比小天才lyk同学的校对工作!!