模板 - 图论
邻接表(链式前向星)存图
定义
struct Allan{
int val;
int to,nxt;
}edge[M];
int idx,head[N];
加边
inline void add(int x,int y,int z)
{
edge[++idx]={z,y,head[x]};
head[x]=idx;
return;
}
遍历出边
for(int i=head[x];i;i=edge[i].nxt)
{
int y=edge[i].to,z=edge[i].val;
...
}
}
树的重心
int size[N],ans=INF;
void DFS(int x)
{
size[x]=1;
int tmp=0;
for(int i=head[x];i;i=edge[i].nxt)
{
int y=edge[i].to;
if(size[y]) continue;
DFS(y);
size[x]+=size[y];
tmp=max(tmp,size[y]);
}
tmp=max(tmp,n-size[x]);
ans=min(ans,tmp);
return;
}
树的直径
DFS 部分
bool vst[N];
void DFS(int x,int *dep)
{
vst[x]=true;
for(int i=head[x];i;i=edge[i].nxt)
{
int y=edge[i].to,z=edge[i].val;
if(vst[y]) continue;
dep[y]=dep[x]+z;
DFS(y,dep);
}
return;
}
主函数部分
DFS(1,depA);
int farNodeA=1;
for(int i=2;i<=n;i++)
if(depA[i]>depA[farNodeA]) farNodeA=i;
memset(vst,false,sizeof(vst));
DFS(farNodeA,depB);
int farNodeB=1;
for(int i=2;i<=n;i++)
if(depB[i]>depB[farNodeB]) farNodeB=i;
printf("%d\n",depB[farNodeB]);
拓扑排序
int deg[N]; //入度
int topOrder[N],topIdx;
queue<int> q;
void TopSort()
{
for(int i=1;i<=n;i++)
if(!deg[i]) q.push(i);
while(!empty())
{
int x=q.front(); q.pop();
topOrder[++topIdx]=x;
for(int i=head[x];i;i=edge[i].nxt)
{
int y=edge[i].to;
deg[y]--;
if(!deg[y]) q.push(y);
}
}
return;
}
LCA
倍增求 LCA
注意需要设置根的深度为 \(1\),或设置 \(0\) 的深度为 \(-1\)。
遍历部分
void LCA_DFS(int x)
{
for(int i=head[x];i;i=edge[i].nxt)
{
int y=edge[i].to;
if(dep[y]) continue;
fa[y][0]=x,dep[y]=dep[x]+1;
LCA_DFS(y);
}
return;
}
预处理部分
inline void LCA_Init()
{
for(int i=1;i<=logN;i++)
for(int x=1;x<=n;x++)
fa[x][i]=fa[fa[x][i-1]][i-1];
return;
}
在线查询部分
inline int LCA(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
for(int i=logN;i>=0;i--)
if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
if(x==y) return x;
for(int i=logN;i>=0;i--)
if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
最短路
SPFA
求单元最短路同时判断源点可达负环
int dist[N],len[N];
bool inq[N];
queue<int> q;
bool SPFA(int src)
{
memset(dist,0x3f,sizeof(dist));
dist[src]=0; q.push(src);
while(!q.empty())
{
int x=q.front(); q.pop();
inq[x]=false;
for(int i=head[x];i;i=edge[i].nxt)
{
int y=edge[i].to,z=edge[i].val;
if(dist[x]+z<dist[y])
{
dist[y]=dist[x]+z;
len[y]=len[x]+1;
if(len[y]>=n) return true;
if(!inq[y])
{
inq[y]=true;
q.push(y);
}
}
}
}
return false;
}
求单源最短路时判断全局负环
int dist[N],len[N];
bool inq[N];
queue<int> q;
bool SPFA(int src)
{
memset(dist,0x3f,sizeof(dist));
dist[src]=0;
for(int i=1;i<=n;i++)
{
inq[i]=true;
q.push(i);
}
while(!q.empty())
{
int x=q.front(); q.pop();
inq[x]=false;
for(int i=head[x];i;i=edge[i].nxt)
{
int y=edge[i].to,z=edge[i].val;
if(dist[x]+z<dist[y])
{
dist[y]=dist[x]+z;
len[y]=len[x]+1;
if(len[y]>=n) return true;
if(!inq[y])
{
inq[y]=true;
q.push(y);
}
}
}
}
return false;
}
Dijkstra
堆优化 Dijkstra
#define PII pair<int,int>
LL dis[N];
bool vst[N];
priority_queue<PII,vector<PII>,greater<PII>> pq;
void Dijkstra(int src)
{
memset(dis,0x3f,sizeof(dis));
dis[src]=0; pq.push({dis[src],src});
while(!pq.empty())
{
int x=pq.top().second; pq.pop();
if(vst[x]) continue;
vst[x]=true;
for(int i=head[x];i;i=edge[i].nxt)
{
int y=edge[i].to,z=edge[i].val;
if(dis[x]+z<dis[y])
{
dis[y]=dis[x]+z;
if(!vst[y]) pq.push({dis[y],y});
}
}
}
return;
}
Floyd
memset(g,0x3f,sizeof(g));
... Input Graph ...
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
传递闭包
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
g[i][j]|=g[i][k]&g[k][j];
最小生成树
Kruskal
void Kruskal()
{
uInit();
sort(raw_edge+1,raw_edge+m+1,cmp);
for(int i=1;i<=m;i++)
{
int x=raw_edge[i].x,y=raw_edge[i].y,z=raw_edge[i].z;
if(uget(x)==uget(y)) continue;
umerge(x,y);
ans+=z;
}
return;
}
Tarjan
Tarjan 求有向图强连通分量
void Tarjan(int x)
{
low[x]=dfn[x]=++ti;
sta[++statop]=x; insta[x]=true;
for(int i=head[x];i;i=edge[i].nxt)
{
int y=edge[i].to;
if(!dfn[y])
{
Tarjan(y);
low[x]=min(low[x],low[y]);
}
else if(insta[y]) low[x]=min(low[x],dfn[y]);
}
if(low[x]==dfn[x])
{
scc_tot++;
int tmp=0;
do
{
tmp=sta[statop--];
insta[tmp]=false;
scc[tmp]=scc_tot;
}while(tmp!=x);
}
}
Tarjan 求割点
void Tarjan(int x)
{
low[x]=dfn[x]=++ti;
sta[++statop]=x;
bool cut_flag=false,can_go=false;
for(int i=head[x];i;i=edge[i].nxt)
{
int y=edge[i].to;
if(!dfn[y])
{
can_go=true;
Tarjan(y);
low[x]=min(low[x],low[y]);
if(dfn[x]<=low[y])
{
if(x!=root || cut_flag) is_cut[x]=true;
cut_flag=true;
vdcc_tot++;
int tmp;
do
{
tmp=sta[statop--];
vdcc[vdcc_tot].push_back(tmp);
}while(tmp!=y)
vdcc[vdcc_tot].push_back(x);
}
}
else low[x]=min(low[x],dfn[y]);
}
if(x==root && !can_go
vdcc[++vdcc_tot].push_back(x);
return;
}
Tarjan 求点双连通分量
int rt;
int dfn[N],low[N],ti;
bool is_cut[N];
int sta[N],top;
int col[N],col_idx;
vector<int> v_dcc[N];
void Tarjan(int x)
{
low[x]=dfn[x]=++ti;
sta[++top]=x;
bool cut_flag=false,can_go=false;
for(int i=head[x];i;i=edge[i].nxt)
{
int y=edge[i].to;
if(!dfn[y])
{
can_go=true;
Tarjan(y);
low[x]=min(low[x],low[y]);
if(low[y]>=dfn[x])
{
if(x!=rt || cut_flag)
is_cut[x]=true;
cut_flag=true;
int tmp=0;
col_idx++;
do
{
tmp=sta[top--];
col[tmp]=col_idx;
v_dcc[col_idx].push_back(tmp);
}while(tmp!=y);
v_dcc[col_idx].push_back(x);
}
}
else low[x]=min(low[x],dfn[y]);
}
if(x==rt && !can_go)
{
col[x]=++col_idx;
v_dcc[col_idx].push_back(x);
}
return;
}
int main()
{
...
for(int i=1;i<=n;i++)
if(!dfn[i]) Tarjan(rt=i);
...
}
Tarjan 求割边/桥
#define rev(id) (((id)&1)?((id)+1):((id)-1))
int dfn[N],low[N],ti;
bool is_cut[M];
void Tarjan(int x,int pre)
{
low[x]=dfn[x]=++ti;
for(int i=head[x];i;i=edge[i].nxt)
{
int y=edge[i].to;
if(!dfn[y])
{
Tarjan(y,i);
low[x]=min(low[x],low[y]);
if(low[y]>dfn[x])
is_cut[i]=is_cut[rev(i)]=true;
}
else if(i!=rev(pre))
low[x]=min(low[x],dfn[y]);
}
return;
}
#undef rev
Tarjan 求边双连通分量
...此处为求割边/桥代码...
int col[N],col_idx;
vector<int> e_dcc[N];
void DFS(int x)
{
e_dcc[col[x]].push_back(x);
for(int i=head[x];i;i=edge[i].nxt)
{
if(is_cut[i]) continue;
int y=edge[i].to;
if(col[y]) continue;
col[y]=col[x];
DFS(y);
}
return;
}
int main()
{
...
for(int i=1;i<=n;i++)
if(!dfn[i]) Tarjan(i,0);
for(int i=1;i<=n;i++)
if(!col[i])
{
col[i]=++col_idx;
DFS(i);
}
...
}
Tarjan 建立圆方树
int dfn[MAXN], low[MAXN], ti;
int sta[MAXN], stp;
void Tarjan(int x)
{
low[x] = dfn[x] = ++ti;
sta[++stp] = x;
for (int i = G.head[x]; i; i = G.edge[i].nxt)
{
int y = G.edge[i].to;
if (dfn[y])
{
low[x] = min(low[x], dfn[y]);
}
else
{
Tarjan(y);
low[x] = min(low[x], low[y]);
if (low[y] == dfn[x])
{
vidx++;
int t = 0;
while (t != y)
{
t = sta[stp--];
T.add(t, vidx);
T.add(vidx, t);
}
T.add(x, vidx);
T.add(vidx, x);
}
}
}
return;
}
二分图
匈牙利算法
bool Hung(int x)
{
for(int i=head[x];i;i=edge[i].nxt)
{
int y=edge[i].to;
if(vst[i]) continue;
vst[y]=true;
if(!match[y] || Hung(match(y)))
{
match[y]=x;
return true;
}
}
return false;
}
本文采用 「CC-BY-NC 4.0」 创作共享协议,转载请注明作者及出处,禁止商业使用。
作者:Jerrycyx,原文链接:https://www.cnblogs.com/jerrycyx/p/18351394

浙公网安备 33010602011771号