因为图论中有些算法的代码比较长,就只贴核心代码html
floydnode
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]); }
spfaweb
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)); } } } }
是从 跑的最短路, 是从 跑的最短路,最后 中存的就是次短路app
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]]);
是从 跑最短路,做为估价函数, 是到如今的真实距离svg
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; }
Kruskalspa
是按照边权排序, 和 是并查集操做,这里不细说code
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; }
严格次小生成树(用倍增实现)xml
一开始要用 作最小生成树,而后在最小生成树上倍增
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]); } } }
求强连通份量( 是 所属的强连通份量编号)
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; } }
判断是否有欧拉(回)路:
有向图,欧拉路:有一个点的入度比出度大
,有一个点的入度比出度小
,其他点的入度等于出度
无向图,欧拉路:有两个点的度数为奇数,其他点的度数为偶数
有向图,欧拉回路:全部点的入度等于出度
无向图,欧拉回路:全部点的度数都是偶数
这些比较基础,就不贴代码了