由于图论中有些算法的代码比较长,就只贴核心代码
最短路
floyd
void floyd()
{
int i,j,k;
for(k=1;k<=n;++k)
for(i=1;i<=n;++i)
for(j=1;j<=n;++j)
a[i][j]=min(a[i][j],a[i][k]+a[k][j]);
}
spfa
void spfa(int s)
{
int x,y,i;
memset(d,0x3f,sizeof(d));
queue<int>q;q.push(s);d[s]=0;
while(!q.empty())
{
x=q.front();q.pop();
vis[x]=false;
for(i=first[x];i;i=nxt[i])
{
y=v[i];
if(d[y]>d[x]+w[i])
{
d[y]=d[x]+w[i];
if(!vis[y])
{
q.push(y);
vis[y]=true;
}
}
}
}
}
dijkstra
priority_queue<pair<int,int> >q;
void dijkstra(int s)
{
int x,y,i;
memset(d,0x3f,sizeof(d));
q.push(make_pair(0,s));d[s]=0;
while(!q.empty())
{
x=q.top().second;q.pop();
for(i=first[x];i;i=nxt[i])
{
y=v[i];
if(d[y]>d[x]+w[i])
{
d[y]=d[x]+w[i];
q.push(make_pair(-d[y],y));
}
}
}
}
次短路
是从 跑的最短路, 是从 跑的最短路,最后 中存的就是次短路
dijkstra(1);
dijkstra(n);
int Min=inf;
for(i=1;i<=n;++i)
for(j=first[i];j;j=nxt[j])
if(d[0][i]+w[j]+d[1][v[j]]!=d[0][n])
Min=min(Min,d[0][i]+w[j]+d[1][v[j]]);
K 短路
是从 跑最短路,作为估价函数, 是到现在的真实距离
int sum=0;
struct node{int x,d;};
bool operator<(const node &p,const node &q) {return dist[p.x]+p.d>dist[q.x]+q.d;}
priority_queue<node>que;
void A_star()
{
que.push((node){1,0});
while(!que.empty())
{
node now=que.top();
que.pop();
if(now.x==n)
{
if(++sum==k)
ans=now.d;
return;
}
for(int i=first[now.x];i;i=nxt[i])
que.push((node){v[i],now.d+w[i]});
}
}
最小生成树
Prim
int Prim()
{
int i,j,Min,pos;
for(i=1;i<=n;++i)
d[i]=a[1][i];
vis[1]=true;
int sum=0;
for(i=1;i<n;++i)
{
Min=inf;
for(j=1;j<=n;++j)
if(!vis[j]&&Min>d[j])
Min=d[j],pos=j;
sum+=d[pos];
vis[pos]=true;
for(j=1;j<=n;++j)
d[j]=min(d[j],a[j][pos]);
}
return sum;
}
Kruskal
是按照边权排序, 和 是并查集操作,这里不细说
int Kruskal()
{
int x,y,i,ans=0;
for(i=1;i<=n;++i)
father[i]=i;
sort(a+1,a+m+1,comp);
for(i=1;i<=m;++i)
{
x=find(a[i].u);
y=find(a[i].v);
if(x!=y)
{
father[x]=y;
ans+=a[i].w;
}
}
return ans;
}
次小生成树
严格次小生成树(用倍增实现)
一开始要用 做最小生成树,然后在最小生成树上倍增
void init()
{
int i,j;
for(j=1;j<=20;++j)
{
for(i=1;i<=n;++i)
{
fa[i][j]=fa[fa[i][j-1]][j-1];
Max1[i][j]=max(Max1[i][j-1],Max1[fa[i][j-1]][j-1]);
Max2[i][j]=max(Max2[i][j-1],Max2[fa[i][j-1]][j-1]);
if(Max1[i][j-1]<Max1[fa[i][j-1]][j-1]&&Max2[i][j]<Max1[i][j-1]) Max2[i][j]=Max1[i][j-1];
if(Max1[i][j-1]>Max1[fa[i][j-1]][j-1]&&Max2[i][j]<Max1[fa[i][j-1]][j-1]) Max2[i][j]=Max1[fa[i][j-1]][j-1];
}
}
}
int find(int x,int y,int limit)
{
int i,ans=0;
if(dep[x]<dep[y]) swap(x,y);
for(i=20;~i;--i)
if(dep[fa[x][i]]>=dep[y])
ans=max(ans,Max1[x][i]!=limit?Max1[x][i]:Max2[x][i]),x=fa[x][i];
if(x==y) return ans;
for(i=20;~i;--i)
{
if(fa[x][i]!=fa[y][i])
{
ans=max(ans,Max1[x][i]!=limit?Max1[x][i]:Max2[x][i]),x=fa[x][i];
ans=max(ans,Max1[y][i]!=limit?Max1[y][i]:Max2[y][i]),y=fa[y][i];
}
}
ans=max(ans,Max1[x][0]!=limit?Max1[x][0]:Max2[x][0]);
ans=max(ans,Max1[y][0]!=limit?Max1[y][0]:Max2[y][0]);
return ans;
}
ll solve(ll MST)
{
int i;
ll ans=inf;
for(i=1;i<=m;++i)
{
if(!vis[i])
{
int temp=find(e[i].u,e[i].v,e[i].w);
if(temp!=e[i].w) ans=min(ans,MST-temp+e[i].w);
}
}
return ans;
}
拓扑排序
是 的入度, 是栈
void Topology()
{
int x,i;
for(i=1;i<=n;++i)
if(!in[i])
stk.push(i);
while(!stk.empty())
{
x=stk.top();stk.pop();
for(i=first[x];i;i=nxt[i])
{
in[v[i]]--;
if(!in[v[i]])
stk.push(v[i]);
}
}
}
Tarjan
求强连通分量( 是 所属的强连通分量编号)
void Tarjan(int x)
{
int i,j;
dfn[x]=low[x]=++num;
stk[++top]=x,insta[x]=true;
for(i=first[x];i;i=nxt[i])
{
j=v[i];
if(!dfn[j])
{
Tarjan(j);
low[x]=min(low[x],low[j]);
}
else if(insta[j])
low[x]=min(low[x],dfn[j]);
}
if(low[x]==dfn[x])
{
sum++;
do
{
i=stk[top--];
id[i]=sum;
insta[i]=false;
}while(i!=x);
}
}
求割点( 是当前搜索树的根, 表示 是否为割点)
void Tarjan(int x)
{
int i,j,child=0;
dfn[x]=low[x]=++num;
for(i=first[x];i;i=nxt[i])
{
j=v[i];
if(!dfn[j])
{
child++,Tarjan(j);
low[x]=min(low[x],low[j]);
if(x==root&&child>1) cut[x]=1;
if(x!=root&&dfn[x]<=low[j]) cut[x]=1;
}
else low[x]=min(low[x],dfn[j]);
}
}
求桥( 表示 是否为桥)
void Tarjan(int x)
{
int i,j;
dfn[x]=low[x]=++num;
for(i=first[x];i;i=nxt[i])
{
j=v[i];
if(i==(from[x]^1))
continue;
if(!dfn[j])
{
from[j]=i,Tarjan(j);
low[x]=min(low[x],low[j]);
if(dfn[x]<low[j])
bridge[i]=bridge[i^1]=true;
}
else low[x]=min(low[x],dfn[j]);
}
}
另一种求边双的写法(和强连通分量差不多)
void Tarjan(int x,int father)
{
int i,j;
dfn[x]=low[x]=++num;
stk[++top]=x,insta[x]=true;
for(i=first[x];i;i=next[i])
{
j=v[i];
if(!dfn[j])
{
Tarjan(j,x);
low[x]=min(low[x],low[j]);
}
else if(insta[j]&&j!=father)
low[x]=min(low[x],dfn[j]);
}
if(low[x]==dfn[x])
{
sum++;
do
{
i=stk[top--];
id[i]=sum;
insta[i]=false;
}
while(i!=x);
}
}
差分约束
差分约束核心是建边,建完边跑 spfa 就行了,不用记板子
不过差分约束还是有一点规律可寻的
- 若求最小值,就跑最长路,并且把所有的约束都化成 的形式
- 若求最大值,就跑最短路,并且把所有的约束都化成 的形式
- 无论是最小值还是最大值,都是从后面往前面建边
可以化成 和 的形式
可以化成 , 可以化成
欧拉回路
(实际上是 UOJ 117 的模板)
边是从 开始计数的,方便后面的操作
求欧拉回路(经过的边记在 中, 是无向图, 是有向图,注意倒着输出,原题还要排除孤立点)
void dfs(int x)
{
for(int &i=first[x];i;i=next[i])
{
int j=v[i],flag=i%2;
int e=(t==1?i/2:i-1);
if(vis[e]) continue;
vis[e]=true,dfs(j);
if(t==1&&flag) ans[++size]=-e;
else ans[++size]=e;
}
}
判断是否有欧拉(回)路:
有向图,欧拉路:有一个点的入度比出度大
,有一个点的入度比出度小
,其余点的入度等于出度
无向图,欧拉路:有两个点的度数为奇数,其余点的度数为偶数
有向图,欧拉回路:所有点的入度等于出度
无向图,欧拉回路:所有点的度数都是偶数
这些比较基础,就不贴代码了