qb培训.图论

图论

授课 zhx


最小生成树

最小生成树包括两个算法:

  • kruskal
  • Prim

两类算法均为基础知识,故不在讲解


严格次小生成树

对于一张有$n$个点$m$条边的无向图,我们要求去掉在去掉一条边的情况下求其最小生成树

也就是说:如果最小生成树选择的边集是$E_M$,严格次小生成树选择的边集是$E_S$,那么需要满足:($value(e)$表示边$e$的权值) (sum_{e∈E_M}value(e) < sum_{e∈E_S}value(e))

1°朴素 O(mnlogn)
首先我们先对原图建出最小生成树,然后依次枚举删除哪一条边,再对原图求最小生成树

2°倍增求LCA O(nlogn)
首先先对原图求出最小生成树,然后依次枚举非树边,求其LCA路径上的最大值与严格次大值,然后再与正在枚举的边的权值计算出值。取历次最小值

3°并查集 
此方法复杂度可达到线性程度,并且要比倍增更加好写
但是……我不会

P4018 严格次小生成树

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>

using namespace std;

const long long   maxn=1e5+5;

struct node{
	long long   x;
	long long   y;
	long long   zh;
}a[3*maxn];

#define INF 2147483647000000

long long   fa[maxn];
long long   f[maxn][30],g[maxn][33],h[maxn][33];
long long   depth[maxn];
bool b[3*maxn];
bool vis[3*maxn];
long long   head[3*maxn],next[3*maxn],ver[3*maxn],edge[3*maxn];
long long   tot,n,m;

inline void add(long long   x,long long   y,long long   z)
{
	ver[++tot]=y;
	edge[tot]=z;
	next[tot]=head[x];
	head[x]=tot;
}

inline bool cmp(node a,node b)
{
	return a.zh < b.zh;
}

inline long long   findd(long long   x)
{
	if(fa[x] != x) fa[x]=findd(fa[x]);
	return fa[x];
}

inline void unite(long long   x,long long   y)
{
	long long   r1=findd(x);
	long long   r2=findd(y);
	if(r1 != r2) fa[r1]=r2;
}

inline void dfs(long long   now,long long   father)
{

	for(long long   i=head[now];i;i=next[i])
	{
		
		long long   v=ver[i],val=edge[i];
		
		if(depth[v]) continue;
		if(v == father) continue;
		
		depth[v] = depth[now]+1;
		
		f[v][0] = now;
		g[v][0] = val;
		h[v][0] = 0;
		
		for(long long   j=1; j<=25 ; j++)
		{
			
			f[v][j] = f[f[v][j-1]][j-1];
			
			if(g[v][j-1]<g[f[v][j-1]][j-1])
			{
				g[v][j] = g[f[v][j-1]][j-1];
				h[v][j] = max(h[f[v][j-1]][j-1] , g[v][j-1]);
			}
			
			if(g[v][j-1]>g[f[v][j-1]][j-1])
			{
				g[v][j] = g[v][j-1];
				h[v][j] = max(h[v][j-1] , g[f[v][j-1]][j-1]);
			}
			
			if(g[v][j-1] == g[f[v][j-1]][j-1])
			{
				g[v][j] = g[v][j-1];
				h[v][j] = max(h[v][j-1] , h[f[v][j-1]][j-1]);	
			}
			
		}
		
		dfs(v,now);
			
	}
	
}

inline long long   get_maxx(long long   p1,long long   p2,long long &ans,long long &cnt)
{
	 ans = -INF;
	 cnt = -INF;
	
	if(depth[p1] < depth[p2] ) 
	{
		p1=p1^p2;
		p2=p1^p2;
		p1=p1^p2;
	}
	
	for(long long   i=25;i>=0;i--)
	{
		if(depth[f[p1][i]] >= depth[p2])
		{	
			int ul=max(g[p1][i],cnt);
			if(g[p1][i] != ul)
			{
				ans=max(g[p1][i],ans);
			}
			if(h[p1][i] != ul)
			{
				ans=max(h[p1][i],ans);
			}
			if(ans != ul)
			{
				ans=max(ans,ans);
			}
			if(cnt != ul)
			{
				ans=max(cnt,ans);
			}
			cnt=ul;
			p1=f[p1][i];
			
		}
	}
	
	if(p1 == p2) return ans;
	
	for(long long   i=25;i>=0;i--)
	{
		if(f[p1][i] != f[p2][i])
		{	
			int ul=max(g[p1][i],cnt);
			if(g[p1][i] != ul)
			{
				ans=max(g[p1][i],ans);
			}
			if(h[p1][i] != ul)
			{
				ans=max(h[p1][i],ans);
			}
			if(ans != ul)
			{
				ans=max(ans,ans);
			}
			if(cnt != ul)
			{
				ans=max(cnt,ans);
			}
			cnt=ul;
			
			ul=max(g[p2][i],cnt);
			if(g[p2][i] != ul)
			{
				ans=max(g[p2][i],ans);
			}
			if(h[p2][i] != ul)
			{
				ans=max(h[p2][i],ans);
			}
			if(ans != ul)
			{
				ans=max(ans,ans);
			}
			if(cnt != ul)
			{
				ans=max(cnt,ans);
			}
			cnt=ul;
			
			p1=f[p1][i];
			p2=f[p2][i];
		}
	}
	
	long long  s=min(g[p1][0],g[p2][0]);
	long long  t=max(g[p1][0],g[p2][0]);
	
	int ul=max(t,cnt);
	if(t != ul)
	{
		ans=max(t,ans);
	}
	if(s != ul)
	{
		ans=max(s,ans);
	}
	if(ans != ul)
	{
		ans=max(ans,ans);
	}
	if(cnt != ul)
	{
		ans=max(cnt,ans);
	}
	cnt=ul;
}

int   main()
{
	
	ios_base::sync_with_stdio(false);
	cout.tie(NULL);
	cin.tie(NULL);
	
	cin>>n>>m;
	
	for(long long   i=1,x,y,z;i<=m;i++)
	{
		cin>>x>>y>>z;
		a[i].x =x;
		a[i].y =y;
		a[i].zh =z;
	}
	
	sort(a+1,a+1+m,cmp);
	
	long long o;
	
	for(int  i=1;i<=n;i++)
	fa[i]=i;
	
	long long k=0,as,sum=0,spite=0;

	for(int   i=1;i<=m;i++)
	{
		long long   v=a[i].x,u=a[i].y;
		if(findd(v) != findd(u))
		{
			unite(u,v);
			k++;
			b[i]=1;
			sum+=a[i].zh ;
			add(u,v,a[i].zh );
			add(v,u,a[i].zh );
			o=u;
		}
		if(k ==n-1) break;
	}
	
	depth[o]=1;
	
	dfs(o,0);
	spite=INF;

	for(int   i=1;i<=m;i++)
	{
		if(b[i] == 0)
		{
			long long cnt,ans;
			get_maxx(a[i].x,a[i].y,ans,cnt);
			if(sum-ans+a[i].zh != sum)
			{
				spite=min(spite,sum-ans+a[i].zh);
			}
			if(sum-cnt+a[i].zh != sum)
			{
				spite=min(spite,sum-cnt+a[i].zh);
			}
			
		}
	}
	
	cout<<spite;
	
}

我直接裂开


最短路

对于最短路算法,熟练掌握一下三种方法

Floyd (全源最短路) O((n^3))

Dijstra + heap优化 (单源最短路,不可处理负边权) O((nlogn))

SPFA (单源最短路,允许存在负边权) O((kE))

差分约束

对于如下不等式

据观察,它们和我们的最短路$dis[u] ≤ dis[u]+s_{<u,v>}$三角形不等式很像

所以我们考虑建图,对每一个不等式,建一个从$i$到$j$的路径为权值$w_{<i,j>}$,然后跑一遍 如果不等式组无解,那么

先上代码:

P5960


#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>

using namespace std;

const int maxn=1e5+5;

int n,m;
bool inque[maxn];
int in[maxn];
int dis[maxn];
int head[maxn],next[maxn],edge[maxn],ver[maxn];
int tot;

inline void add(int x,int y,int z)
{
	ver[++tot] = y;
	edge[tot] = z;
	next[tot] = head[x];
	head[x] = tot;
}

inline bool  spfa(int o)
{
	memset(dis,0x3f,sizeof(dis));
	memset(inque,0,sizeof(inque));
	memset(in,0,sizeof(in));
	
	queue<int> q;
	dis[o]=0;
	inque[o]=1;
	in[o]=1;
	q.push(o);
	
	while(q.size())
	{
		int aux=q.front();
		q.pop();
		inque[aux]=0;
		for(int i=head[aux];i;i=next[i])
		{
			
			if(dis[ver[i]] > dis[aux] + edge[i] )
			{
				dis[ver[i]]=dis[aux] + edge[i];
				if(inque[ver[i]] != 1)
				{
					q.push(ver[i]);
					inque[ver[i]]=1;
					in[ver[i]]++;
					if(in[ver[i]] >= n+1) 
					return false;
				 } 
				
			}
			
		}
	}
	
	return true;
	
	
}

int main()
{
	ios_base::sync_with_stdio(false);
	cout.tie(NULL);
	cin.tie(NULL);
	
	cin>>n>>m;
	
	for(int i=1,x,y,z;i<=n;i++)
	{
		cin>>x>>y>>z;
		add(y,x,z);
	}
	
	for(int i=1;i<=n;i++)
	{
		add(0,i,0);
	}
	
	if(spfa(0) == false)
	{
		cout<<"NO";
	}
	else
	{
		for(int i=1;i<=n;i++)
		cout<<dis[i]<<endl;
	}
	
	
}

-end-

原文地址:https://www.cnblogs.com/-Iris-/p/13469140.html