图论(CSP-S难度)知识小结(完善中)

图论知识小结

  • 序言——先扯几句闲话
  • --最短路--
    • FLOYD
    • SPFA
    • Dijkstra
    • 次短路
    • 差分约束系统
    • K短路
    • 分层图
    • 优化搜索(?)
  • --生成树--
    • Prim
    • Kruskal
    • 次小生成树
    • 最小树形图
    • 最小乘积生成树
    • 最优比率生成树
  • --连通性--
    • SCC
    • BCC
  • --网络流--
    • 最大流
  • --匹配--
    • 2-SAT
    • 二分图
  • --其他--
    • LCA
    • 拓扑排序
    • 基环树
    • 线段树优化
    • 剩余系
    • 离散化

序言——先扯几句闲话

第一次写知识总结,有什么毛病烦请诸位神仙指正(
博客里有些链接来自题目,有些来自我已经写过的博客,如果是博客我就没有再放题解了(懒得搬了~
图论两个重要的问题,一是算法的选择,二是建图。很多算法往往本身有局限性,例如Dijkstra一般不能求含负边的图的最短路,而SPFA一般又不能维护稠密图最短路。
然而图论最重要的不是算法的选择,而是建图。很多人为算法是否可用或者是否好写而常常烦恼,然而建图往往决定了你能拿多少分。
当然两者都很重要,只是近年来的考题都更加注重思维,所以建图变得更为重要。

–最短路–

求解最短路只是一种思想,于我而言,这其实这是在图上DP的过程
最短路的建图关键在于如何利用已知信息构造辅助的边和点,例如我们经常用到的虚点

FLOYD

Floyd复杂度O(N^3),适合求解多源最短路,不过这个算法近来已经不是很常考,有时候会出现倍增Floyd,或者传递闭包
例如奶牛接力这道题,倍增Floyd的板题,利用类似矩阵快速幂的思想,优化到 O ( N 3 ∗ l o g N ) O(N^3*logN) O(N3logN)
利用Floyd支持多次查询、修改、删边的图论题也有出现

SPFA

SPFA是一个被Oiers诟病太多了的算法,号称复杂度 O ( k M ) O(kM) O(kM),实际上常常被出题人卡成sb,不过她确实还是好用,例如最短路计数,最短路Dp,最短路+二分,这几类题往往不会以稠密图的方式出现,因此SPFA 有时也能快的飞起

Dijkstra

本身Dijkstra的复杂度其实是 O ( N 2 ) O(N^2) O(N2)的,不过用优先队列或者Set优化可以降低到 O ( N ∗ l o g N ) O(N*logN) O(NlogN),Dijkstra的效率是有很高程度的保证的,不过也难逃诟病——求含负边权的图的最短路时,显得束手无策,当然在一定限制下也能求,不过就不讨论了。
Dijkstra除了复杂度相对稳定,还有一点就是它相对于SPFA更容易维护字典序,因为优先队列里面存的是一个二元组,第二元在第一元相同的情况下,可以保证编号小的点优先

次短路

这个其实很好办,在最短路的算法里面多维护一个dis2表示次短路即可,板题为Roadblocks

差分约束系统

差分约束系统是基于不等式组而来的一种最短/长路问题,例如给定m个ai-aj的不等式,求a1-an的最大值
建图方式其实已经烂大街了,就是套路:求最大值,所有不等式转为"<=",对于ai-aj<=k,j向i连一条长为k的边,然后跑最短路;求最小值,所有不等式转为">=",对于ai-aj>=k,j向i连一条长为k的边,然后跑最长路
特别地,对于等式,转化成一个"<=",一个">="来连边
虽然说是套路,但放在题目当中还是一道题一个样
几道水题:[SCOI2011]糖果、[SCOI2008]天平、小K的农场

K短路

这种题一般出现在有向图中
主要就是用A* 算法优化,先跑反图求出dis,在以此和到终点的距离为估价函数求前k短路,[SDOI2010]魔法猪学院就是这样一道题
然而A* 算法并没有达到复杂度最优,因为我们只利用了距离这一信息,最优的目前是可持久化可并堆的做法,建议大家看看zxyoi神仙的博客

分层图

分层图其实没什么好讲的,其实本质就是最短路DP,只不过分层图的辅助空间用在加边上了,DP的话直接在Dis数组上加一维,例如可以免费k条边的最短路
一道经典题——逛公园

优化搜索(?)

能够把图论题考到这种高度也是绝了
不过NOIP确实做到了——华容道

–生成树–

前置知识:并查集
这一部分理解起来本身挺容易的,不过拓展非常蛇皮,需要很多 奇技淫巧,建图的关键在于对边权的修改

Prim

Prim是一种贪心算法,对于每一个新加入树的点,要满足它到树的距离最小,每次选取最小值,直到每个点在树上,堆优化之后优化至 O ( ( N + M ) l o g M ) O((N+M)logM) O((N+M)logM),完全图较为常用

Kruskal

Kruskal是另一种贪心算法,按边权排序之后依次加入,不过边要能改变连通性再加,直至加入n-1条边,复杂度 O ( M l o g + M α ( N ) ) O(Mlog+Mα(N)) O(Mlog+Mα(N)),这种算法较之前者更好写,复杂度在大多数情况下表现更好

次小生成树

首先我们可以证明次小生成树一定在一颗最小生成树的邻集中(即只有一条边不同)。
所以我们可以先求出最小生成树,枚举每一条非树边,利用lca维护非树边两端点之间路径上的最大值和次大值,并尝试用当前非树边替换,最后打擂台比较,得到最小生成树邻集中边权和最小的一颗生成树,就是次小生成树。
详见【模板】次小生成树

最小树形图

即有向图的最小生成树
例如WOJ 4104
给定包含 n 个结点, m 条有向边的一个图。试求一棵以结点r 为根的最小树形图,并输出最小树形图每条边的权值之和,如果没有以 r 为根的最小树形图,输出 −1。

#include
#define re register
#define ll long long
using namespace std;
inline int rd(){
	int re data=0,w=1;static char ch=0;ch=getchar();
	while(!isdigit(ch)&&ch!='-')ch=getchar();
	if(ch=='-')w=-1,ch=getchar();
	while(isdigit(ch))data=(data<<1)+(data<<3)+(ch^48),ch=getchar();
	return data*w;
}
const int N=105,M=1e4+5,inf=1e9;
struct node{int u,v,w;}e[M];
int n,m,rt,cnt,fa[N],id[N],mn[N],top[N];
inline int solve(){
	ll re ans=0;
	while(1){
		for(int re i=1;i<=n;i++)id[i]=top[i]=0,mn[i]=inf;
		for(int re i=1;i<=m;i++)if(e[i].u!=e[i].v&&e[i].w<mn[e[i].v])fa[e[i].v]=e[i].u,mn[e[i].v]=e[i].w;
		int re u=mn[rt]=0;
		for(int re i=1;i<=n;i++){
			if(mn[i]==inf)return -1;//An Abandoned Point
			ans+=mn[i];
			for(u=i;u!=rt&&top[u]!=i&&(!id[u]);u=fa[u])top[u]=i;
			if(u!=rt&&!id[u]){
				id[u]=++cnt;//Find These Rings
				for(int re j=fa[u];j!=u;j=fa[j])id[j]=cnt;
			}
		}
		if(!cnt)return ans;//Answer Comes Out When There Is No Ring
		for(int re i=1;i<=n;i++)if(!id[i])id[i]=++cnt;//Make Self-rings
		for(int re i=1;i<=m;i++){
			int re last=mn[e[i].v];//Contract Points
			if((e[i].u=id[e[i].u])!=(e[i].v=id[e[i].v]))e[i].w-=last;//Not In The Same Ring
		}n=cnt;rt=id[rt];cnt=0;
	}
}
int main(){
	n=rd();m=rd();rt=rd();
	for(int re i=1;i<=m;i++)e[i]=(node){rd(),rd(),rd()};
	cout<<solve();
	return 0;
}

最小乘积生成树

顾名思义,就是求suma*sumb的最小值,其中a,b为两种边权
详见Timeismoney

最优比率生成树

最优比率生成树是基于分数规划而来的一类问题
要求最优比率,可以考虑二分这个比率,用二分值修改边权,再代入求MST去验证
题目重建家园

–连通性–

这一部分的建图往往要与生成树、最短路(常涉及缩点操作)相联系

SCC

tarjan算法的意义在于对树的dfs序进行了有效的利用,算法实现不再赘述,因为确实在模板方面没有什么特别之处
例题:受欢迎的牛

BCC

边双例题:【模板】BCC边双连通分量、矿场搭建
点双例题:困难的图

–网络流–

这一类题一般都不会出现在联赛难度的题目当中,不过有些贪心题可以用网络流打打暴力,这时候建边关键在于流量的大小和点的含义

最大流

跑最大流就是暴力求解贪心的一种常规操作,不过真正解最大流还是有必要会的
虽然思想很显然,但是建边的花样非常非常多
拆点、修改边权……网络流几乎把所有的建图方式都覆盖了
例题:蜥蜴lizard

–匹配–

匹配类的问题在建边方面没有太大的扩展性,主要是根据限制关系建图

2-SAT

2-SAT问题的关键在于处理选与不选的关系
例题:Ikki’s Story IV - Panda’s Trick

二分图

二分图是图论中一种比较特殊的模型,求解这类问题时,往往要考虑与最大匹配,最小点/边覆盖等模型的联系
板子题很简单:[USACO4.2]完美的牛栏The Perfect Stall
不过加上一个线段树分治就比较毒了……:【bzoj4025】二分图&题解

–其他–

LCA

LCA不会单独考,但是结合其他内容之后就会异常的毒
例如 :加一个线段树合并——【NOIP2016DAY1T2】天天爱跑步
还有之前提到过的次小生成树

拓扑排序

拓扑排序的题一般不单独拿出来考,往往与其他图论题相联系
例如:
构造汇点(虚点)——表格
Tarjan+拓扑排序+DP——[ZJOI2007]最大半连通子图

基环树

2018年NOIPDAY2T1的旅行就是一道基环树相关问题,只不过前60pts白送,剩下40pts也只是多想一下就能到手(然而当时我太菜,并没有想到这一点,还在纠结为什么没过有环的那个样例……)
基环树的题与一般的树的题相比差异不是太大,主要是要多考虑一个环的问题,然而代码非常难写,没有固定的模板(除了找环,如果你认为那个DFS也算)

线段树优化

看到这里应该都知道我在说哪道题:
Journeys
题解

剩余系

这方面最经典的就是墨墨的等式
利用同余的思想求了一个最短路,妙啊
题解在这儿

离散化

离散化坐标是解决坐标系中点对距离的一种建图方式,往往按照坐标上的一些关系建图可以大大降低建图复杂度。
例如:连通代价,原本 O ( N 2 ) O(N^2) O(N2)的建图复杂度,由于三个维度的坐标可以分开考虑,建图复杂度就降低至了 O ( N ) O(N) O(N)

你可能感兴趣的:(---图论---,总结)