自己码的图论板子(实时更新)
更新日志
2020.8.15 上传了 链式前向星,Floyd算法,Prim算法, 并查集,Kruskal,Dijkstra,BellmanFord算法,spfa,dp求树的直径,bfs求分层图,匈牙利算法,欧拉路,倍增LCA,Tarjan,Dinic,费用流,线段树,树链剖分
2020.8.17 小幅度改动了Kruskal和并查集的算法
2020.8.18 用struct中置函数优化了并查集
2020.8.19 用struct中放置函数又花了线段树,树链剖分,更改kru中使用并查集部分,更改了树链剖分的部分命名,对每条边的信息追加了起点u
2020.8.20 修复了费用流的bug
约定
不再对标有废弃的板子更新,但是此板子为正确且可套用的,只是出现了上级取代而已
一个图的点数为n,一个图的边数为m
边的具体信息通过 edge储存
一条边的起点为u,终点为v,权值信息为c
遍历边起点为x,终点为y
到点的距离通过 dis储存
分层图信息通过 d 储存
时间戳信息通过 dfn 储存
一个点是否被使用用used 一条边是否被使用用usedWay
需要建立反向图的情况在信息后面全部加2,如edge2,search2
根节点信息用 root ,源点用S,汇点用T
临时变量用tmp,临时队列用que,临时栈用s
涉及具体数据结构遍历名称随单词拼接大写
于栈中用 ins,于队列中用 inque
点的度为deg ,出度为outdeg 入度为indeg
前置内容
#define MAXN 25000//点数
#define MAXM (2*MAXN)//道路数
//#define int long long
typedef pair<int,int> pii;
const int inf = 0x3f3f3f3f;
#define dis(i,j) (sqrt(pow((nod[i].x-nod[j].x),2)+pow((nod[i].y-nod[j].y),2)))
struct {
double x,y;
}nod[MAXN];//用于点以坐标表示的时候
链式前向星
#define search(i,y) for(int i=head[y];i;i=edge[i].next) //遍历一个点的出边
#define add(u,v,c) {edge[++cnt]={u,v,c,head[u]};head[u]=cnt;}//加入一条边
int head[MAXN],cnt=1;//0说明没有下一个值
struct EDGE{
int u,v,c;
int next;//下一个值的位置;
}edge[MAXM];
Floyd算法
//floyd算法求任意两点之间的最短距离
//因为floyd求的一般都是稠密图,储存方式邻接矩阵
//floyd也可以用来在点数较小的情况下求区间闭包和可达性
int edge[MAXN][MAXN]; //邻接矩阵
int dis[MAXN][MAXN];
void Floyd(int n){// 编号从1 开始
memcpy(dis,edge,sizeof(edge));
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}
Prim算法
//从点的角度求最小生成树,复杂度为O(N^2),储存方式邻接矩阵
//在稠密图中效果优于kru,稀疏图中劣于kru
//可以用二叉堆优化到O(M+N)但是涉及到边了不如直接上kru
int edge[MAXN][MAXN];//邻接矩阵存边
bool used[MAXN]={0};
int d[MAXN];
int Prim(int n){
memset(d,inf,sizeof(d));
memset(used,0,sizeof(used));
d[1]=0;
for(int i=1;i<n;i++){ //一共只需要进行n-1次操作
int x=0;
for(int j=1;j<=n;j++)
if(!used[j]&&(x==0||d[x]>d[j])) //遍历找出没有用过并且距离已选遍最近的点
x=j;
used[x]= true;
for(int y=1;y<=n;y++)
if(!used[y])d[y]=min(d[y],edge[x][y]);
}
int ans=0;
for(int i=1;i<=n;i++){
ans+=d[i];
if(ans>=inf)return -1;//图不连通则返回-1
}
return ans;
}
并查集
struct DisjointSet {
int n;
int root[MAXN];
void init(int x){
n=x;
for(int i=1;i<=n;i++)root[i]=i;
}
int find(int x){
if(x!=root[x])
root[x]=find(root[x]);
return root[x];
}
void merge(int x,int y){root[find(x)]=find(y);}//将x合并到y中}
bool judge(int x,int y){return find(x)==find(y);}
}grp;
Kruskal
//Kruskal算法 从边的角度算最小生成树
//复杂度为O(m) 需要并查集作为辅助
typedef pair<int,pair<int ,int > > pi_ii;
int n;
vector<pair<int,pair<int,int> > > edge;
int kruskal(int m){//m为边数
int ans=0;
grp.init(n);
priority_queue<pi_ii ,vector<pi_ii>,greater<pi_ii > > que;
for(auto i:edge)
que.push(i);
while(!que.empty()){
pi_ii tmp;
tmp=que.top();que.pop();
int u=tmp.second.first,v=tmp.second.second;
if(!grp.judge(u,v)){
grp.merge(u,v);
ans+=tmp.first;
}
}
}