图论
基本概念
-
完全图:任意两点均有边相连, n n n 个节点
的完全图的边数为 n ∗ ( n − 1 ) / 2 n*(n-1)/2 n∗(n−1)/2 。 -
连通图:任意两点均能直接或间接到达,区别于完全图任意两点必须直接到达的定义。
-
树:树是一个联通且无环的图,边数为 n − 1 n-1 n−1 。
二叉树遍历
-
先序遍历:根 — — — 左儿子 — — — 右儿子
-
中序遍历:左儿子 — — — 根 — — — 右儿子
-
后序遍历:左儿子 — — — 右儿子 — — — 根
三种遍历方式其实就是 递归 的实现,以中序遍历为例,具体过程为:
-
先中序遍历根节点的左子树
-
再访问此左子树的根节点
-
最后中序遍历右子树
最短路径
(以下基本为代码)
F l o y d Floyd Floyd在稠密图效果最佳,边权可正可负,但不能判负环。
【核心代码】
基本模型都会,所以只给出输出路径的代码:
void floyed()
{
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(f[i][k]+f[k][j]<f[i][j]){
f[i][j]=f[i][k]+f[k][j];
pre[i][j]=pre[k][j];// 存路径
}
}
void print(int x)
{
if(!pre[s][x])
return;
print(pre[s][x]);
printf("->%d",x);
}
int main()
{
cin>>n>>m>>s>>e;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
f[i][j]=inf;
for(int i=1;i<=m;i++){
int u,v,w;
cin>>u>>v>>w;
f[u][v]=w;
pre[u][v]=u;
}
floyed();
printf("%d",f[s][e]);
printf("%d",s);
print(e);
return 0;
}
在讲 D i j k s t r a Dijkstra Dijkstra 和 S P F A SPFA SPFA 前,先讲一个便利的存图方式 − − − --- −−− 链式前向星
链式前向星 是一种特殊的存边方式,它可以看做 “带有索引数组的多个数据链表”构成的结构集合。
在一个具有 n n n个点 m m m 条边的有向图结构中,可以把每条边所属的“类别”定义为该点的起点标号。这样所有的边被分成 n n n 类,其中第 x x x 类就由“从 x x x 出发的所有边”构成。通过表头 h e a d [ x ] head[x] head[x] ,可以很容易的定位到第 x x x 类对应的链表,从而访问从 x x x 出发的所有边。
【核心代码】
struct st
{
int to;
int dis;
int nxt;
}edge[MAXM];
int head[MAXN],size;
void add_size(int from,int to,int dis)
{
edge[++size].nxt=head[from];
edge[size].to=to;
edge[size].dis=dis;
head[from]=size;
}
void init()
{
for(int i=1;i<MAXN;i++)
edge[i].nxt=head[i]=0;
return;
}
int main()
{
init();
int n,m,s;
cin>>n>>m>>s;
for(int i=1;i<=m;i++){
int u,v,w;
cin>>u>>v>>w;
add_edge(u,v,w);
}
for(int i=head[s];i;i=edge[i].nxt){
int v=edge[i].to,w=edge[i].dis;
//do something
}
return 0;
}
回归正题~~
D i j k s t r a Dijkstra Dijkstra 用于求解单源最短路,不能处理负边,也不能判负环。
【核心代码】
struct st
{
int to;
int dis;
int nxt;
}edge[MAXM];
int head[MAXN],size;
void add(int from,int to,int dis)
{
edge[++size].nxt=head[from];
edge[size].to=to;
edge[size].dis=dis;
head[from]=size;
}//用链式前向星存图
int n,m,s,e;
int c[MAXN];
bool b[MAXN];
void init()
{
cin>>n>>m>>s>>e;
memset(head,-1,sizeof(head));
for(int i=1;i<=MAXN;i++)
c[i]=inf;
for(int i=1;i<=m;i++){
int u,v,w;
cin>>u>>v>>w;
add(u,v,w);
}
return;
}
void dij()
{
c[s]=0;//起点
for(int i=1;i<n;i++){
int minl=inf,k=0;
for(int j=1;j<=n;j++)
if(!b[j]&&c[j]<minl){
minl=c[j];
k=j;
}
if(!k)
break;
b[k]=1;
for(int i=head[k];~i;i=edge[i].nxt){
int v=edge[i].to,w=edge[i].dis;
c[v]=min(c[v],c[k]+w);
}
}
}
int main()
{
init();
dij();
cout<<c[e];//终点
return 0;
}
S P F A SPFA SPFA 可以处理负边,也可以判负环,但最坏复杂度为 O ( n ∗ m ) O(n*m) O(n∗m) ,所以在没有负边权的情况下,尽量使用堆优化的 D i j k s t r a Dijkstra Dijkstra 来求单源最短路。
【核心代码】
struct st
{
int to;
int dis;
int nxt;
}edge[MAXM];
int head[MAXN],size;
void add(int from,int to,int dis)
{
edge[++size].nxt=head[from];
edge[size].to=to;
edge[size].dis=dis;
head[from]=size;
}//链式前向星存图
int n,m,s,e;
void init()
{
scanf("%d%d%d%d",&n,&m,&s,&e);
for(int i=1;i<=MAXN;i++)
head[i]=-1;
for(int i=1;i<=MAXM;i++)
edge[i].nxt=-1;
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
}
}
int c[MAXN];
bool b[MAXN];
void spfa()
{
queue<int> q;
for(int i=1;i<=n;i++)
c[i]=inf;
c[s]=0;//起点
b[s]=1;
q.push(s);
while(!q.empty())
{
int u=q.front();
q.pop();
b[u]=0;
for(int i=head[u];~i;i=edge[i].nxt){
int v=edge[i].to,w=edge[i].dis;
if(w+c[u]<c[v]){
c[v]=w+c[u];
if(!b[v]){
q.push(v);
b[v]=1;
}
}
}
}
}
int main()
{
init();
spfa();
printf("%d",c[e]);//终点
return 0;
}
最短路的题多种多样,解法也是,优化的方法还有许多许多,根据题意选择合适的方法即可。
以上两道题为最短路模板,第一道题的数据更强,可以练习一下。
以上是蒟蒻总结的图论的知识,都是最基本的,还是熟练地掌握为好。