……

模板整理——图论部分

- 图论

- 最短路算法1

- 最短路算法2

  • Floyd 算法(全源最短路)(\(\mathcal O(n^3)\)
#include"iostream"
#include"cstdio"
#include"cmath"
using namespace std;

#define MAXN 105

int e[MAXN][MAXN];
int n,m;
int u,v,w;

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) e[i][j]=1000000000;
	for(int i=1;i<=n;i++) e[i][i]=0;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&u,&v,&w);
		e[u][v]=min(e[u][v],w);
	}
	for(int k=1;k<=n;k++)//注意枚举顺序
	{
		for(int j=1;j<=n;j++)
		{
			for(int i=1;i<=n;i++)
			{
				e[i][j]=min(e[i][j],e[i][k]+e[k][j]);
			}
		}
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++) printf("%d ",e[i][j]);
		puts("");
	}
	return puts(""),0;
}
  • Dijkstra (单源最短路)(朴素实现为 \(\mathcal O(n^2)\),经过优化(一般是堆优化)可以做到 \(\mathcal O(n\log n)\),但不可以处理负权边)
#include"iostream"
#include"cstdio"
#include"cmath"
#include"queue"
using namespace std;

#define MAXN 100005 

int n,m,s;
int u,v,c;
int dis[MAXN],vis[MAXN];
struct node
{
	int to,nxt,w;
}e[MAXN<<1];
int head[MAXN],cnt=0;
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >q;

void add(int u,int v,int w){e[++cnt].to=v,e[cnt].w=w,e[cnt].nxt=head[u],head[u]=cnt;}

void dij()
{
	while(!q.empty())
	{
		int u=q.top().second;
		q.pop();
		if(vis[u]) continue;
		vis[u]=1;
		for(int i=head[u];i;i=e[i].nxt)
		{
			int j=e[i].to;
			if(dis[j]>dis[u]+e[i].w)
			{
				dis[j]=dis[u]+e[i].w;
				q.push(make_pair(dis[j],j));
			}
		}
	}
	return;
}

int main()
{
	scanf("%d%d%d",&n,&m,&s);
	for(int i=1;i<=m;i++) scanf("%d%d%d",&u,&v,&c),add(u,v,c);
	for(int i=1;i<=n;i++) dis[i]=0x7fffffff;
	dis[s]=0,q.push(make_pair(0,s));
	dij();
	for(int i=1;i<=n;i++) printf("%d ",dis[i]);
	return puts(""),0;
}
  • SPFA(单源最短路)(是对 Bellman-Ford 算法的一种优化,在随机数据下期望复杂度是 \(\mathcal O(kn)\),但容易被卡成 \(\mathcal O(n^2)\),故慎用,但优势是可以处理负权边)
#include"iostream"
#include"cstdio"
#include"cmath"
#include"queue"
using namespace std;

#define MAXN 100005 

int n,m,s;
int u,v,c;
int dis[MAXN],vis[MAXN];
struct node
{
	int to,nxt,w;
}e[MAXN<<1];
int head[MAXN],cnt=0;
queue<int>q;

void add(int u,int v,int w){e[++cnt].to=v,e[cnt].w=w,e[cnt].nxt=head[u],head[u]=cnt;}

void SPFA()
{
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		vis[u]=0;
		for(int i=head[u];i;i=e[i].nxt)
		{
			int j=e[i].to;
			if(dis[j]>dis[u]+e[i].w)
			{
				dis[j]=dis[u]+e[i].w;
				if(!vis[j]) q.push(j),vis[j]=1;
			}
		}
	}
	return;
}

int main()
{
	scanf("%d%d%d",&n,&m,&s);
	for(int i=1;i<=m;i++) scanf("%d%d%d",&u,&v,&c),add(u,v,c);
	for(int i=1;i<=n;i++) dis[i]=0x7fffffff;
	dis[s]=0,q.push(s);
	SPFA();
	for(int i=1;i<=n;i++) printf("%d ",dis[i]);
	return puts(""),0;
}

- 最小生成树(MST 问题)

  • Kruskal (\(\mathcal O(m\log m)\)
#include"algorithm"
#include"iostream"
#include"cstdio"
#include"cmath"
using namespace std;

int n,m;
int sum,tot;
int fa[5005];
struct node
{
	int u,v,w;
}e[200005];

bool cmp(node n,node m){return n.w<m.w;}

void init(int n){for(int i=1;i<=n;i++) fa[i]=i;}
int getf(int u){return fa[u]=(fa[u]==u)?u:getf(fa[u]);}

int merge(int u,int v)
{
	int t1=getf(u),t2=getf(v);
	if(t1^t2){fa[t2]=t1;return 1;}
	return 0;
}

int main()
{
	scanf("%d%d",&n,&m);
	init(n);
	for(int i=1;i<=m;i++) scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
	sort(e+1,e+m+1,cmp);
	for(int i=1;i<=m;i++)
	{
		if(merge(e[i].u,e[i].v))
		{
			tot++,sum+=e[i].w;
			if(tot==n-1) break;
		}
	}
	if(tot<n-1) puts("orz");
	else printf("%d\n",sum);
	return 0;
}
  • Prim(使用堆优化 \(\mathcal O(n\log n)\)
#include"algorithm"
#include"iostream"
#include"cstdio"
#include"cmath"
#include"queue"
using namespace std;

#define MAXN 200005

int n,m;
int sum,tot;
int u,v,w;
struct node
{
    int to,nxt,w;
}e[MAXN<<1];
int head[MAXN],cnt=0;
int dis[MAXN],vis[MAXN];
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >q;

void add(int u,int v,int w){e[++cnt].to=v,e[cnt].w=w,e[cnt].nxt=head[u],head[u]=cnt;}

int Prim()
{
	while(!q.empty()&&tot!=n)
	{
		int u=q.top().second,c=q.top().first;
		q.pop();
		if(vis[u]) continue;
		vis[u]=1;
		tot++,sum+=c;
		for(int i=head[u];i;i=e[i].nxt)
		{
			int j=e[i].to;
			if(dis[j]>e[i].w) dis[j]=e[i].w,q.push(make_pair(dis[j],j));
		}
	}
	if(tot==n) return 1;//注意这里指的是n个点而非边 
	else 0;
}

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&u,&v,&w);
		add(u,v,w),add(v,u,w);
	}
	for(int i=1;i<=n;i++) dis[i]=0x7fffffff;
	dis[1]=0,q.push(make_pair(0,1));
	int flag=Prim();
	if(!flag) puts("orz");
	else printf("%d\n",sum);
	return 0;
}

- 负环判断\(\mathcal O(kn)\)

利用玄学的已死的 没死光的 SPFA 进行判断。

#include"iostream"
#include"cstring"
#include"cstdio"
#include"queue"
#include"cmath"
using namespace std;

#define MAXN 2005
#define inf 0x7fffffff
#define read(x) scanf("%d",&x)
#define mem(s) memset(s,0,sizeof(s))

int n,m;
int u,v,w;
int t,dis[MAXN],len[MAXN];
int vis[MAXN];
queue<int> q;
struct node
{
    int to,nxt,w;
}e[MAXN<<2];
int head[MAXN],cnt=0;

void add(int u,int v,int w){e[++cnt].to=v,e[cnt].w=w,e[cnt].nxt=head[u];head[u]=cnt;}

int SPFA()
{
    while(!q.empty())
    {
        int u=q.front();
        q.pop(),vis[u]=0;
        for(int i=head[u];i;i=e[i].nxt)
        {
            int j=e[i].to;
            if(dis[j]>dis[u]+e[i].w)
            {
                dis[j]=dis[u]+e[i].w,len[j]=len[u]+1;
                if(len[j]>n) return 1;
                if(!vis[j]) vis[j]=1,q.push(j);
            }
        }
    }
    return 0;
}

int main()
{
    read(t);
    while(t--)
    {
        read(n),read(m);
        mem(vis),mem(len),mem(head),mem(e);
        while(!q.empty()) q.pop();
        cnt=0;
        for(int i=1;i<=m;i++)
        {
        	read(u),read(v),read(w);
        	if(w>=0) add(v,u,w);
        	add(u,v,w);
		}
        for(int i=1;i<=n;i++) dis[i]=inf;
        dis[1]=0,vis[1]=1,q.push(1);
        (SPFA())?puts("YES"):puts("NO");
    }
    return 0;
}

- 差分约束系统\(\mathcal O(kn)\)

#include"iostream"
#include"cstdio"
#include"cmath"
using namespace std;

#define MAXN 5005
#define read(x) scanf("%d",&x)

int n,m;
int x,y,z;
struct node
{
	int to,nxt,w;
}e[MAXN<<1];
int head[MAXN],cnt=0;
int dis[MAXN],vis[MAXN];
int cou[MAXN]={0},que[MAXN*MAXN],he=1,ta=1;

void add(int u,int v,int w)
{
	e[++cnt].to=v;
	e[cnt].nxt=head[u];
	e[cnt].w=w;
	head[u]=cnt;
}

void SPFA()
{
	que[1]=0,vis[0]=1,dis[0]=0;
	while(he>=ta)
	{
		int u=que[ta++];
		vis[u]=0;
		for(int i=head[u];i;i=e[i].nxt)
		{
			int j=e[i].to;
			if(dis[j]>dis[u]+e[i].w)
			{
				dis[j]=dis[u]+e[i].w,cou[j]=cou[u]+1;
				if(!vis[j]) que[++he]=j,vis[j]=1;
				if(cou[j]>=n+2){puts("NO");return;}
			}	
		}	
	}
	for(int i=1;i<=n;i++) printf("%d ",dis[i]);
	puts("");
	return;
}

int main()
{
	read(n),read(m);
	for(int i=1;i<=m;i++) read(x),read(y),read(z),add(y,x,z);
	for(int i=1;i<=n;i++) add(0,i,0);
	for(int i=1;i<=n;i++) dis[i]=1e9;
	SPFA();
	return 0;
}

- 树的直径

  • 两次 dfs(\(\mathcal O(n)\)
#include"iostream"
#include"cstdio"
#include"cmath"
#include"cstring"
using namespace std;

#define MAXN 10005
#define read(x) scanf("%d",&x)

int n,u,v;
int len=0,pos;
struct node
{
    int to,nxt;
}e[MAXN<<1];
int head[MAXN],cnt=0;

void add(int u,int v){e[++cnt].to=v,e[cnt].nxt=head[u],head[u]=cnt;}

void dfs(int cur,int fa,int stp)
{
    if(stp>len) len=stp,pos=cur;
    for(int i=head[cur];i;i=e[i].nxt)
    {
        int j=e[i].to;
        if(j==fa) continue;
        dfs(j,cur,stp+1);
    }
}

int main()
{
    read(n);
    for(int i=1;i<n;i++) read(u),read(v),add(u,v),add(v,u);
    dfs(1,0,0),len=0,dfs(pos,0,0);
    printf("%d\n",len);
    return 0;
}
  • 树形 dp(\(\mathcal O(n)\)
#include"iostream"
#include"cstdio"
#include"cmath"
#include"cstring"
using namespace std;

#define MAXN 10005
#define read(x) scanf("%d",&x)

int n,u,v;
int dp[MAXN],ans=0;
struct node
{
    int to,nxt;
}e[MAXN<<1];
int head[MAXN],cnt=0;

void add(int u,int v){e[++cnt].to=v,e[cnt].nxt=head[u],head[u]=cnt;}

void dfs(int cur,int fa)
{
    int maxn=0,maxnn=0;
    for(int i=head[cur];i;i=e[i].nxt)
    {
        int j=e[i].to;
        if(j==fa) continue;
        dfs(j,cur);
        if(maxn<=dp[j]+1) maxnn=maxn,maxn=dp[j]+1;
        else if(maxnn<dp[j]+1) maxnn=dp[j]+1;
    }
    dp[cur]=maxn,ans=max(ans,maxn+maxnn);
}

int main()
{
    read(n);
    for(int i=1;i<n;i++) read(u),read(v),add(u,v),add(v,u);
    dfs(1,0);
    printf("%d\n",ans);
    return 0;
}

- 树的重心(\(\mathcal O(n)\)

这题模板题是一道私题,我就不放了,这里只放代码。

#include"iostream"
#include"cstdio"
#include"cstring"
#include"algorithm"
using namespace std;

#define read(x) scanf("%d",&x)
#define MAXN 500005

int n;
int d[MAXN],num,minx=0x7fffffff;
struct node 
{
	int to,nxt;
}e[MAXN<<1];
int head[MAXN],cnt=0;
int x,y,c=0,cen[15];

void add(int u,int v)
{
	e[++cnt].to=v;
	e[cnt].nxt=head[u];
	head[u]=cnt;
}

int dfs(int cur,int fa)
{
	d[cur]=1;
	int maxn=0;
	for(int i=head[cur];i;i=e[i].nxt)
	{
		int j=e[i].to;
		if(j==fa) continue;
		int now=dfs(j,cur);
		maxn=max(maxn,now);
		d[cur]+=now;
	}
	maxn=max(maxn,n-d[cur]);
	if(maxn<minx) minx=maxn,num=cur,c=1,cen[c]=cur;
	else if(maxn==minx) c++,cen[c]=cur;
	return d[cur];
}

int main()
{
	read(n);
	for(int i=1;i<n;i++) read(x),read(y),add(x,y),add(y,x);
	dfs(1,0);
	sort(cen+1,cen+c+1);
	for(int i=1;i<=c;i++) printf("%d ",cen[i]);
	return puts(""),0;
}

- 最近公共祖先(LCA)

  • 倍增(\(\mathcal O(n\log n)\)

这是传统写法,然而我不会。

  • tarjan(离线做法)(\(\mathcal O(n\alpha(n)+q)\)
#include"iostream"
#include"cstdio"
#include"cmath"
using namespace std;

#define MAXN 500005

int n,root,qe;
int u,v;
int f[MAXN],vis[MAXN];
struct node
{
	int to,nxt;
}e[MAXN<<1];
int head[MAXN],cnt=0;
struct query
{
	int to,nxt,id;
}q[MAXN<<1];
int sta[MAXN],cntt=0;
int ans[MAXN];

void add1(int u,int v){e[++cnt].to=v,e[cnt].nxt=head[u],head[u]=cnt;}
void add2(int u,int v,int rt){q[++cntt].to=v,q[cntt].nxt=sta[u],q[cntt].id=rt,sta[u]=cntt;}

void init(int n){for(int i=1;i<=n;i++) f[i]=i,vis[i]=0;}
int getf(int u){return f[u]=(f[u]==u)?u:getf(f[u]);}
void merge(int u,int v){int t1=getf(u),t2=getf(v);if(t1^t2) f[t2]=t1;}

void dfs(int cur,int fa)
{
	vis[cur]=1; 
	for(int i=head[cur];i;i=e[i].nxt) 
	{
		int j=e[i].to;
		if(j==fa) continue;
		dfs(j,cur);
		merge(cur,j);//千千万万别写反了 
	}
	for(int i=sta[cur];i;i=q[i].nxt)
	{
		int j=q[i].to;
		if(vis[j]) ans[q[i].id]=getf(j);
	}
	return;
}

int main()
{
	scanf("%d%d%d",&n,&qe,&root);
	init(n);
	for(int i=1;i<n;i++) scanf("%d%d",&u,&v),add1(u,v),add1(v,u);
	for(int i=1;i<=qe;i++) scanf("%d%d",&u,&v),add2(u,v,i),add2(v,u,i);
	dfs(root,root);
	for(int i=1;i<=qe;i++) printf("%d\n",ans[i]);
	return 0;
}
  • 树链剖分(\(\mathcal O(n\log n)\)
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;

#define read(x) scanf("%d",&x)
#define MAXN 500005

int top[MAXN],dep[MAXN],son[MAXN],vis[MAXN],f[MAXN],tot[MAXN];
struct node
{
	int to,nxt;
}e[MAXN<<1];
int head[MAXN],cnt=0;
int n,m,root,l,r;

void add(int u,int v)
{
	e[++cnt].to=v;
	e[cnt].nxt=head[u];
	head[u]=cnt;
}

int dfsa(int cur,int deep)
{
	vis[cur]=1;
	dep[cur]=deep;
	tot[cur]=1;
	son[cur]=0;
	int maxn=0;
	for(int i=head[cur];i;i=e[i].nxt)
	{
		int j=e[i].to;
		if(vis[j]) continue;
		f[j]=cur;
		int now=dfsa(j,deep+1);
		if(now>maxn) maxn=now,son[cur]=j;
		tot[cur]+=now;
	}
	return tot[cur];
}

void dfsb(int cur,int topf)
{
	vis[cur]=1;
	top[cur]=topf;
	if(son[cur]) dfsb(son[cur],topf);
	for(int i=head[cur];i;i=e[i].nxt)
	{
		int j=e[i].to;
		if(vis[j]) continue;
		dfsb(j,j);
	}
	return;
}

int lca(int x,int y)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		x=f[top[x]];
	}
	return dep[x]<dep[y]?x:y;
}

int main()
{
	read(n),read(m),read(root);
	for(int i=1;i<n;i++)
	{
		read(l),read(r);
		add(l,r),add(r,l);
	}
	memset(vis,0,sizeof(vis));
	f[root]=root;
	dfsa(root,1);
	memset(vis,0,sizeof(vis));
	dfsb(root,root);
	for(int i=1;i<=m;i++)
	{
		read(l),read(r);
		printf("%d\n",lca(l,r));
	}	
	return 0; 
}
  • ST 表(\(\mathcal O(n\log n)\)

学那么多干啥,有两种方法就够了/kk。

- 缩点(tarjan)(\(\mathcal O(n+m)\)

缩的就是你,强联通分量!

#include"iostream"
#include"cstdio"
#include"cmath"
#include"cstring"
using namespace std;

#define read(x) scanf("%d",&x)
#define MAXN 10005
#define mem(s) memset(s,0,sizeof(s))

int n,m;
int head[MAXN],cnt=0;
int cou=0,a[MAXN],id=0;
struct node
{
	int to,nxt;
}e[MAXN*10];
int be[MAXN],si[MAXN],val[MAXN],vis[MAXN];
int low[MAXN],num[MAXN];
int x[MAXN*10],y[MAXN*10];
int s[MAXN],top=0;
int q[MAXN],rt=0,ta=1,in[MAXN];
int dp[MAXN],ans=0;

void add(int u,int v)
{
	e[++cnt].to=v;
	e[cnt].nxt=head[u];
	head[u]=cnt;
}

void dfs(int cur)
{
	low[cur]=num[cur]=++id;
	vis[cur]=1,s[++top]=cur;
	for(int i=head[cur];i;i=e[i].nxt)
	{
		int j=e[i].to;
		if(!vis[j]) dfs(j);
		if(!be[j]) low[cur]=min(low[cur],low[j]);
	}
	if(low[cur]==num[cur])
	{
		cou++;
		while(1)
		{
			int j=s[top--];
			be[j]=cou,val[cou]+=a[j],si[cou]++;
			if(j==cur) break;
		}
	}	
}

int main()
{
	read(n),read(m);
	for(int i=1;i<=n;i++) read(a[i]);
	for(int i=1;i<=m;i++)
	{
		read(x[i]),read(y[i]);
		add(x[i],y[i]);
	}
	for(int i=1;i<=n;i++) if(!vis[i]) dfs(i);
	cnt=0,mem(head),mem(e);
	for(int i=1;i<=m;i++) if(be[x[i]]!=be[y[i]]) add(be[x[i]],be[y[i]]),in[be[y[i]]]++;
	for(int i=1;i<=cou;i++) if(!in[i]) q[++rt]=i,dp[i]=val[i],ans=max(ans,dp[i]);
	while(ta<=rt)
	{
		int u=q[ta];
		ta++;
		for(int i=head[u];i;i=e[i].nxt)
		{
			int j=e[i].to;
			in[j]--;
			dp[j]=max(dp[j],dp[u]+val[j]),ans=max(ans,dp[j]);
			if(!in[j]) q[++rt]=j;
		}
	}
	printf("%d\n",ans);
	return 0;
}

- 割点(tarjan)(\(\mathcal O(n+m)\)

#include"algorithm"
#include"iostream"
#include"cstdio"
#include"cmath"
using namespace std;

#define MAXN  20005
#define read(x) scanf("%d",&x)

int n,m;
int low[MAXN],num[MAXN],vis[MAXN];
int head[MAXN],cnt=0;
int c=0;
struct node
{
    int to,nxt;
}e[MAXN*10];
int u,v;
int id=0;
int child=0;
int ma[MAXN];

void add(int u,int v){e[++cnt].to=v,e[cnt].nxt=head[u],head[u]=cnt;}

void dfs(int cur,int fa)
{
    num[cur]=low[cur]=++id;
    vis[cur]=1;
	int child=0;
    for(int i=head[cur];i;i=e[i].nxt)
    {
        int j=e[i].to;
        if(!vis[j]) 
		{
			child++;
			dfs(j,cur);
        	low[cur]=min(low[cur],low[j]);
    		if(fa!=cur&&low[j]>=num[cur]&&!ma[cur]) c++,ma[cur]=1;
		}
		else if(j!=fa) low[cur]=min(low[cur],num[j]); 
	}
	if(cur==fa&&child>=2&&!ma[cur]) c++,ma[cur]=1;
    return;
}

int main()
{
    read(n),read(m);
    for(int i=1;i<=m;i++) read(u),read(v),add(u,v),add(v,u);
    for(int i=1;i<=n;i++) if(!vis[i]) dfs(i,i);
    printf("%d\n",c);
    for(int i=1;i<=n;i++) if(ma[i]) printf("%d ",i);
    return puts(""),0;
}

无向图缩点用 tarjan 割点类似的方法实现。

割边(桥)的判断方法是 low[j]>num[cur],但不需要特判根节点。

- 二分图匹配

  • 匈牙利算法(\(\mathcal O(ne)\)
#include"iostream"
#include"cstdio"
#include"cmath"
using namespace std;

#define MAXN 505
#define read(x) scanf("%d",&x)

int n,m,k;
int u,v;
struct node
{
    int to,nxt;
}e[50005];
int head[MAXN],cnt=0;
int mark[MAXN],vis[MAXN];
int ans=0;

void add(int u,int v){e[++cnt].to=v,e[cnt].nxt=head[u],head[u]=cnt;}

bool dfs(int cur)
{
    for(int i=head[cur];i;i=e[i].nxt)
    {
        int j=e[i].to-n;
        if(vis[j]) continue;
        vis[j]=1;
        if(!mark[j]||dfs(mark[j])){mark[j]=cur;return true;}
    }
    return false;
}

int main()
{
    read(n),read(m),read(k);
    for(int i=1;i<=k;i++) read(u),read(v),add(u,n+v);
    for(int i=1;i<=n;i++)
    {
     	for(int j=1;j<=m;j++) vis[j]=0;
     	if(dfs(i)) ans++;
    }
    return printf("%d\n",ans),0;
}
  • Dinic(\(\mathcal O(n\sqrt{e})\)

看什么看我又不会网络流。

posted @ 2020-10-29 23:02  童话镇里的星河  阅读(188)  评论(0编辑  收藏  举报