由于图论中有些算法的代码比较长,就只贴核心代码
最短路
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));
}
}
}
}
次短路
d 0 d_0 d0 是从 1 1 1 跑的最短路, d n d_n dn 是从 n n n 跑的最短路,最后 m i n min min 中存的就是次短路
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 短路
d i s t dist dist 是从 n n n 跑最短路,作为估价函数, d d d 是到现在的真实距离
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
s o r t sort sort 是按照边权排序, f a t h e r father father 和 f i n d find find 是并查集操作,这里不细说
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;
}
次小生成树
严格次小生成树(用倍增实现)
一开始要用 k r u s k a l kruskal kruskal 做最小生成树,然后在最小生成树上倍增
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;
}
拓扑排序
i n i in_i ini 是 i i i 的入度, s t k stk stk 是栈
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
求强连通分量( i d i id_i idi 是 i i i 所属的强连通分量编号)
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);
}
}
求割点( r o o t root root 是当前搜索树的根, c u t i cut_i cuti 表示 i i i 是否为割点)
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]);
}
}
求桥( b r i d g e i bridge_i bridgei 表示 i i i 是否为桥)
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 就行了,不用记板子
不过差分约束还是有一点规律可寻的
- 若求最小值,就跑最长路,并且把所有的约束都化成 ≥ ≥ ≥ 的形式
- 若求最大值,就跑最短路,并且把所有的约束都化成 ≤ ≤ ≤ 的形式
- 无论是最小值还是最大值,都是从后面往前面建边
x − y = z x-y=z x−y=z 可以化成 x − y ≥ z x-y\geq z x−y≥z 和 x − y ≤ z x-y\leq z x−y≤z 的形式
x − y > z x-y>z x−y>z 可以化成 x − y ≥ z + 1 x-y\geq z+1 x−y≥z+1, x − y < z x-y<z x−y<z 可以化成 x − y ≤ z − 1 x-y\leq z-1 x−y≤z−1
欧拉回路
(实际上是 UOJ 117 的模板)
边是从 2 2 2 开始计数的,方便后面的操作
求欧拉回路(经过的边记在 a n s ans ans 中, t = 1 t=1 t=1 是无向图, t = 2 t=2 t=2 是有向图,注意倒着输出,原题还要排除孤立点)
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;
}
}
判断是否有欧拉(回)路:
有向图,欧拉路:有一个点的入度比出度大
1
1
1,有一个点的入度比出度小
1
1
1,其余点的入度等于出度
无向图,欧拉路:有两个点的度数为奇数,其余点的度数为偶数
有向图,欧拉回路:所有点的入度等于出度
无向图,欧拉回路:所有点的度数都是偶数
这些比较基础,就不贴代码了