图论基础.

21 篇文章 0 订阅

图论

基本概念

  • 完全图:任意两点均有边相连, n n n 个节点
    的完全图的边数为 n ∗ ( n − 1 ) / 2 n*(n-1)/2 n(n1)/2

  • 连通图:任意两点均能直接或间接到达,区别于完全图任意两点必须直接到达的定义。

  • 树:树是一个联通且无环的图,边数为 n − 1 n-1 n1

二叉树遍历

  • 先序遍历:根 — — 左儿子 — — 右儿子

  • 中序遍历:左儿子 — — — — 右儿子

  • 后序遍历:左儿子 — — 右儿子 — —

三种遍历方式其实就是 递归 的实现,以中序遍历为例,具体过程为:

  1. 先中序遍历根节点的左子树

  2. 再访问此左子树的根节点

  3. 最后中序遍历右子树

最短路径

(以下基本为代码)

  • F l o y d Floyd Floyd

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

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

S P F A SPFA SPFA 可以处理负边,也可以判负环,但最坏复杂度为 O ( n ∗ m ) O(n*m) O(nm) ,所以在没有负边权的情况下,尽量使用堆优化的 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;
}

最短路的题多种多样,解法也是,优化的方法还有许多许多,根据题意选择合适的方法即可。

洛谷P4779

洛谷P3371

以上两道题为最短路模板,第一道题的数据更强,可以练习一下。


以上是蒟蒻总结的图论的知识,都是最基本的,还是熟练地掌握为好。

完结撒花~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值