NOIP模板复习——图论

因为图论中有些算法的代码比较长,就只贴核心代码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));
			}
		}
	}
}

次短路

d 0 d_0 是从 1 1 跑的最短路, d n d_n 是从 n n 跑的最短路,最后 m i n min 中存的就是次短路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]]);

K 短路

d i s t dist 是从 n n 跑最短路,做为估价函数, d d 是到如今的真实距离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

s o r t sort 是按照边权排序, f a t h e r father f i n d find 是并查集操做,这里不细说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

一开始要用 k r u s k a l 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 i i 的入度, s t k 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 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 是当前搜索树的根, c u t i cut_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 表示 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\geq z x y z x-y\leq z 的形式

x y > z x-y>z 能够化成 x y z + 1 x-y\geq z+1 x y < z x-y<z 能够化成 x y z 1 x-y\leq z-1

欧拉回路

(其实是 UOJ 117 的模板)

边是从 2 2 开始计数的,方便后面的操做

求欧拉回路(通过的边记在 a n s ans 中, t = 1 t=1 是无向图, 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 ,其他点的入度等于出度
无向图欧拉路:有两个点的度数为奇数,其他点的度数为偶数
有向图欧拉回路:全部点的入度等于出度
无向图欧拉回路:全部点的度数都是偶数

这些比较基础,就不贴代码了