图论模板
加了部分博客链接
1.图的存储:
邻接矩阵: g[a][b]存储点a,b间的有关信息(权值或该两点间是否有边)稠密图
邻接表: 类似哈希表的拉链法,每个点都有一个单链表,存储这个点可以走到的点(包括直接走到和间接走到)。
邻接表的结构体实现
struct node{
int value;//存储边的权值
int to;//存储该边的终点
int next;//存储下一条边的编号
}a[N];
int cnt=0;
int head[N];//存储以i为起点的边的编号
void add(int u,int v,int value){
a[cnt].to=v;
a[cnt].value=value;
a[cnt].next=head[u];
head[u]=cnt++;//以当前点为起点的编号为cnt
//无向图,正反各存一遍
a[cnt].to=u;
a[cnt].value=value;
a[cnt].next=head[v];
head[v]=cnt++;
}
链式前向星:
int h[N],e[N],ne[N],idx;
void init(){
idx=0;
memset(h,-1,sizeof h);
}
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx,idx++;
}
2.图的深度优先遍历和广度优先遍历
3.拓扑排序
有向无环图=拓扑图
手写队列
int h[N],e[N],ne[N],idx,ans=N,n,m;
int d[N],q[N];
void init(){
idx=0;
memset(h,-1,sizeof h);
}
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
bool topsort(){
int hh=0,tt=-1;
for(int i=1;i<=n;i++)
if(!d[i]) q[++tt]=i;
while(hh<=tt){
int t=q[hh++];
for(int i=h[t];i!=-1;i=ne[i]){
int j=e[i];
if(--d[j]==0) q[++tt]=j;
}
}
return tt==n-1;
}
4.最短路算法
最短路问题分为单源最短路和多源汇最短路。前者是求某一点到其他点的最短距离,后者是起点终点不确定,任选一组起点终点求最短路。
n点m边
Dijkstra
所有边权均是正数的单源最短路 O(nn)
适用于稠密图 即m和nn接近
int n,m;///n个点 m条边
int g[maxn][maxn];///邻接矩阵建图
int dis[maxn];///记录当前点到起点的距离
bool st[maxn];///若当前点的最短距离已经确定 为true
int dijkstra(){
memset(dis,0x3f,sizeof dis);///初始化距离为正无穷
dis[1]=0;///从1开始更新
for(int i=0;i<n-1;i++){
int t=-1;
///找到当前不在st中而且距离最小的点t
for(int j=1;j<=n;j++)
if(!st[j]&&(t==-1||dis[t]>dis[j]))
t=j;
///用点t更新其他不在st中的点的距离
for(int j=1;j<=n;j++)
dis[j]=min(dis[j],dis[t]+g[t][j]);
///将t点放到st集合里
st[t]=true;
}
///如果该点距离为正无穷 说明没有更新过
if(dis[n]==0x3f3f3f3f) return -1;
return dis[n];
}
堆优化的Dijkstra
所有边权均是正数的单源最短路 O(mlogn)
适用于稀疏图 即m和n一个数量级
int n,m;
int h[maxn],w[maxn],e[maxn],ne[maxn],idx;
int dis[maxn];
bool st[maxn];
void add(int a,int b,int c){
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int dijkstra(){
memset(dis,0x3f,sizeof dis);
dis[1]=0;
///建立一个维护最小值的优先队列
priority_queue<PII,vector<PII>,greater<PII>>heap;
heap.push({0,1});///起始点放入队列
while(heap.size()){
auto t=heap.top();///最小值
heap.pop();
int ver=t.second,d=t.first;
if(st[ver]) continue;///该点更新
st[ver]=true;
for(int i=h[ver];i!=-1;i=ne[i]){
int j=e[i];
if(dis[j]>d+w[i]){
dis[j]=d+w[i];
heap.push({dis[j],j});
}
}
}
if(dis[n]==0x3f3f3f3f) return -1;
return dis[n];
}
Bellman Ford
存在负权边/限制边数 O(n*m)
适合于m和n一个数量级
struct node{
int a,b,c;
}e[maxx];
int n,m,k;
int dis[maxn],last[maxn];///保证更新只是从上一次更新,不会被以前的影响
void BellmanFord(){
memset(dis,0x3f,sizeof dis);
dis[1]=0;
for(int i=0;i<k;i++){
memcpy(last,dis,sizeof dis);
for(int j=0;j<m;j++){
auto t=e[j];
dis[t.b]=min(dis[t.b],last[t.a]+t.c);
}
}
}
SPFA
存在负权边 (相当于队列优化的Bellman Ford)
int n, m;
int h[N], w[N], e[N], ne[N], idx;
int dist[N];
bool st[N];
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
int spfa()
{
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
queue<int> q;
q.push(1);
st[1] = true;
while (q.size())
{
int t = q.front();
q.pop();
st[t] = false;
for (int i = h[t]; i != -1; i = ne[i])
{
int j = e[i];
if (dist[j] > dist[t] + w[i])
{
dist[j] = dist[t] + w[i];
if (!st[j])
{
q.push(j);
st[j] = true;
}
}
}
}
return dist[n];
}
Floyd
Floyd 多源汇 O(n^3) 最短路
int n,m,q;
int d[maxn][maxn];
void init(){
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i==j) d[i][j]=0;
else d[i][j]=inf;
}
void Floyd(){
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
}
4.最小生成树算法
prim算法
int n, m;
int g[N][N];
int dist[N];
bool st[N];
int prim(){
memset(dist, 0x3f, sizeof dist);
int res = 0;
for (int i = 0; i < n; i ++ )
{
int t = -1;
for (int j = 1; j <= n; j ++ )
if (!st[j] && (t == -1 || dist[t] > dist[j]))
t = j;
if (i && dist[t] == INF) return INF;
if (i) res += dist[t];
st[t] = true;
for (int j = 1; j <= n; j ++ ) dist[j] = min(dist[j], g[t][j]);
}
return res;
}
kruskal
O(mlogm)
int n,m;
int root[maxn];
struct node{
int a,b,w;
}a[maxn];
bool cmp(node a,node b){
return a.w<b.w;
}
int Find(int x){
if(x!=root[x]) root[x]=Find(root[x]);
return root[x];
}
int kruskal(){
sort(a,a+m,cmp);
for(int i=1;i<=n;i++) root[i]=i;
int res=0,cnt=0;
for(int i=0;i<m;i++){
int aa=a[i].a;
int b=a[i].b;
int w=a[i].w;
aa=Find(aa),b=Find(b);
if(aa!=b){
root[aa]=b;
res+=w;
cnt++;
}
}
if(cnt<n-1) return INF;
else return res;
}
5.染色法判定二分图
链式前向星存图
#include<bits/stdc++.h>
using namespace std;
const int maxn=200010;
int n,m;
int h[100010],e[maxn],ne[maxn],idx;
int col[100010];
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
bool dfs(int u,int c){
col[u]=c;
for(int i=h[u];~i;i=ne[i]){
int j=e[i];
if(!col[j]){
if(!dfs(j,3-c)) return 0;
}
else if(col[j]==c) return 0;
}
return 1;
}
int main(){
cin>>n>>m;
memset(h,-1,sizeof h);
while(m--){
int x,y;
cin>>x>>y;
add(x,y);add(y,x);///无向图
}
bool flag=1;
for(int i=1;i<=n;i++)
if(!col[i]){
if(!dfs(i,1)){
flag=0;
break;
}
}
if(flag) puts("Yes");
else puts("No");
return 0;
}
邻接矩阵存图
偷的学长的(超小声)
vector<int>v[maxn];///vector存个图
int vis[maxn];///vis标记颜色
ll n,m;
bool dfs(int u,int c)
{
vis[u]=c;
int sz=v[u].size();
for(int i=0;i<sz;i++)
{
int e=v[u][i];
if(vis[e]==c) return false;
if(!vis[e]&&!dfs(e,-c)) return false;
}
return true;
}
int main()
{
scanf("%lld%lld",&n,&m);
for(int i=1;i<=m;i++){
int x,y;scanf("%d%d",&x,&y);
v[x].push_back(y);
v[y].push_back(x);
}
for(int i=1;i<=n;i++)///图可能不全是联通的 判断全部子图均为二分图才可以
if(vis[i]==0&&!dfs(i,1)){
printf("No");
return 0;
}
printf("Yes\n");
return 0;
}
#pragma GCC optimize(2)
#pragma GCC optimize("Ofast","unroll-loops","omit-frame-pointer","inline")
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll,ll>PLL;
typedef pair<int,int>PII;
#define I_int ll
#define modl 19260817*19890604-19491001
inline ll read()
{
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
char F[200];
inline void out(I_int x) {
if (x == 0) return (void) (putchar('0'));
I_int tmp = x > 0 ? x : -x;
if (x < 0) putchar('-');
int cnt = 0;
while (tmp > 0) {
F[cnt++] = tmp % 10 + '0';
tmp /= 10;
}
while (cnt > 0) putchar(F[--cnt]);
//cout<<" ";
}
const int maxn=1e6+7;///总点个数,随题意改
const int MAX=1<<26;///最大值,随题意改
///链式前向星存储边
///表示u->v 边权为c(容量) 下一条边
struct Edge{
ll u,v,c,ne;
};
///求最大流
struct Dinic{
int n,m;///点数,边数
int edn;///建图时所用边数
int p[maxn];///链式前向星存图的父节点
int d[maxn];///分层建图时表示的层数
int sp,tp;///原点 汇点
Edge edge[maxn*6];///存储边
///初始化
void init(int sp,int tp){
///this->sp=sp;//可省去
/// this->tp=tp;
edn=0;///清空建图时计边的计数器
/// memset(p,-1,sizeof p);
memset(p,-1,sizeof(int)*(n+2));///小优化 仅初始化使用的空间
}
void addedge(int u,int v,int c){///建图加边
edge[edn]={u,v,c,p[u]};p[u]=edn++;///正向一定要加的
edge[edn]={v,u,0,p[v]};p[v]=edn++;///无向图改成c 反悔边
}
///分层建图并且寻找增广路的过程
int bfs(){
queue<int>q;
while(!q.empty()) q.pop();///清空队列
memset(d,-1,sizeof d);///初始化距离数组
d[sp]=0;q.push(sp);///进行分层建图
while(!q.empty()){
int cur=q.front();q.pop();
for(int i=p[cur];~i;i=edge[i].ne){
int u=edge[i].v;
if(d[u]==-1&&edge[i].c>0){///容量>0才会有贡献
d[u]=d[cur]+1;
q.push(u);
}
}
}
return d[tp]!=-1;///是否存在增广路
}
ll dfs(ll a,ll b){
ll r=0;
if(a==tp) return b;///到达汇点
for(int i=p[a];~i&&r<b;i=edge[i].ne){
int u=edge[i].v;
if(edge[i].c>0&&d[u]==d[a]+1){
///只拓展下一层 并且容量>0才会有贡献
int x=min(edge[i].c,b-r);///可以增加的流量
x=dfs(u,x);
r+=x;///统计流量
///更新边权:找到反向边
///奇数异或1相当于-1,偶数异或1相当于+1
edge[i].c-=x;///回溯时更新
edge[i^1].c+=x;///成对变换
}
///if(!r) break;
}
if(!r) d[a]-=2;///uncertain
return r;
}
ll Maxflow(){
ll total=0,t;
while(bfs()){
while(t=dfs(sp,MAX)) total+=t;///增广找到流量
}
return total;
}
}dinic;
int main(){
dinic.n=read(),dinic.m=read();
dinic.sp=read(),dinic.tp=read();
dinic.init(dinic.sp,dinic.tp);
for(int i=1;i<=dinic.m;i++){
ll u=read(),v=read(),w=read();
dinic.addedge(u,v,w);
}
ll t=dinic.Maxflow();
cout<<t;
return 0;
}
struct edge{
int e,ne;
};
struct Flower{
queue<int>q;
edge e[maxn*maxn];
int h[maxn],root[maxn],mat[maxn],pre[maxn],vis[maxn],dfn[maxn];
int n,m,idx,timetemp,res;
inline void init(){
timetemp=res=idx=0;
memset(h,0,sizeof h);
}
inline void add(int u,int v){
e[++idx]={v,h[u]};h[u]=idx;
}
inline int Find(int x){
if(x!=root[x]) root[x]=Find(root[x]);
return root[x];
}
inline int LCA(int u,int v){
++timetemp;u=Find(u),v=Find(v);
while(dfn[u]!=timetemp){
dfn[u]=timetemp;
u=Find(pre[mat[u]]);
if(v) swap(u,v);
}
return u;
}
void Blossom(int x,int y,int w){
while(Find(x)!=w){
pre[x]=y;y=mat[x];
if(vis[y]==2) vis[y]=1,q.push(y);
if(x==Find(x)) root[x]=w;
if(y==Find(y)) root[y]=w;
x=pre[y];
}
}
int Aug(int s){
if((res+1)*2>n) return 0;
for(int i=1;i<=n;i++) root[i]=i,vis[i]=pre[i]=0;
while(!q.empty()) q.pop();
q.push(s);vis[s]=1;
while(!q.empty()){
int u=q.front();q.pop();
for(int i=h[u];i;i=e[i].ne){
int v=e[i].e;
if(Find(u)==Find(v)||vis[v]==2) continue;
if(!vis[v]){
vis[v]=2;pre[v]=u;
if(!mat[v]){
for(int x=v,lst;x;x=lst) lst=mat[pre[x]],mat[x]=pre[x],mat[pre[x]]=x;
return 1;
}
vis[mat[v]]=1;q.push(mat[v]);
}
else{
int w=LCA(u,v);
Blossom(u,v,w);Blossom(v,u,w);
}
}
}
return 0;
}
};
LCA
struct LCA
{
int fa[maxn][25], dep[maxn];
int e[maxn],ne[maxn],m;
int h[maxn], idx = 0,n,root;
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;///链式前向星存图
}
void init()
{
memset(h, -1, sizeof h);
idx = 0;
for(int i=1;i<n;i++){
int u=read,v=read;
add(u,v);add(v,u);
}
}
void bfs(int root)
{
queue<int>q;
memset(dep, 0x3f, sizeof dep);
dep[0] = 0; ///特殊处理
dep[root] = 1;
q.push(root);
while(!q.empty())
{
int t = q.front();
q.pop();
for(int i = h[t]; ~i; i = ne[i])
{
int j = e[i];
if(dep[j] > dep[t] + 1)
{
dep[j] = dep[t] + 1;
q.push(j);
fa[j][0] = t; ///预处理fa数组
for(int k = 1; k <= 15; k++)
fa[j][k] = fa[fa[j][k - 1]][k - 1];
}
}
}
}
int lca(int a, int b)
{
if(dep[a] < dep[b]) swap(a, b);
for(int k = 15; k >= 0; k--)
if(dep[fa[a][k]] >= dep[b]) a = fa[a][k]; ///使a,b跳到同一层
if(a == b) return a;
for(int k = 15; k >= 0; k--)
if(fa[a][k] != fa[b][k]) a = fa[a][k], b = fa[b][k];
return fa[a][0];
}
};
部分模板来自AcWing
未完待续