学习笔记:分层图(主要是最短路)

不能再咕咕了,学了点好东西。

引入

分层图是啥呢?
看到几乎没有讲这个的(那我也不讲了),那我简单说下吧
将节点分在不同有特点的层次中形成的图。
就是这样子的:


可以发现,每层都是一样的,当然,这不是分层图的唯一构造,具体的话......看后边吧

分层图最短路

讲的清楚的真的少......

一般模型

对一个图,可以选择若干条边进行转换,求最短路。
一脸懵逼??
那就看题吧。

(T1).

题目链接:P4568 [JLOI2011]飞行路线
看起来毫无头绪,只因为不会分层图。
我们依题意构造(k+1)层相同原边权的图,然后每层有联系的点(即相连的点)连上边权为(0)的边,跑常规最短路即可。
注意的事:
(1).沟通不同层之间的边只连相邻层,且方向是从编号小的层到编号大的层。
其实很简单,我们如果往上走就浪费次数了且一步用多次免费也是不允许的。
(2).如果没有用(k)次免费就到了,怎么办?
我们考虑将每一层的终点与下一层(或最后一层)的终点连上边权为(0)的点。
这样,如果没有用完即到达(即到达的终点不在最后一层),这样保证有一个花费为(0)的“虚假路径”。
(盗张图,这位大佬的图没有连终点,那就脑补一条从(4)(9)的有向边吧:
对于样例:

其实就是很套路的题啦。

(Code):

#include<iostream>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
const int MAXN=220005;
const int inf=2147483647;
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >q;
int dis[MAXN],vis[MAXN]={0};
int n,m,k,s,t;
int l,r,g;
struct node
{
	int to,nxt,w;
}e[50005*42+15];
int head[MAXN],cnt=0;
void add(int u,int v,int c)
{
	e[++cnt].to=v;
	e[cnt].nxt=head[u];
	e[cnt].w=c;
	head[u]=cnt;
}
void dij()
{
	while(!q.empty())
	{
		int g=q.top().second;
		q.pop();
		if(vis[g]) continue;
		vis[g]=1;
		for(int i=head[g];i;i=e[i].nxt)
		{
			int j=e[i].to;
			if(dis[j]>dis[g]+e[i].w)
			{
				dis[j]=dis[g]+e[i].w;
				q.push(make_pair(dis[j],j));
			}
		}
	}
	return;
}
int main()
{
	scanf("%d%d%d%d%d",&n,&m,&k,&s,&t);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&l,&r,&g);
		add(l,r,g),add(r,l,g); 
		for(int j=1;j<=k;j++)
		{
			add(n*(j-1)+l,n*j+r,0);
			add(n*(j-1)+r,n*j+l,0);
			add(n*j+l,n*j+r,g);
			add(n*j+r,n*j+l,g);
		}
	}
	for(int i=1;i<=k;i++) add(n*(i-1)+t,n*i+t,0);
	q.push(make_pair(0,s));
	for(int i=0;i<=n*(k+1);i++) dis[i]=inf; 
	dis[s]=0;
	dij();
	printf("%d
",dis[n*k+t]);
	return 0;
}

分析:时间复杂度是多少呢?
显然这个图有((k+1)n)个节点,那么复杂度是(O(knlog kn))
那么一般来说,空间开多大保险呢?
对于点数组(存点信息的)有((k+1)n)个点,要开这么大,
对于边数组(存边信息的)(2(k+1)n)((k+1)层无向图)(+2kn)(沟通两层的,来来回回,比如有(3--4)的边,就要在(3.i)(4.i+1)之间与(4.i)(3.i+1)之间连边)(+k)(沟通终点的边)(=(4k+2)n+k),可以当结论记住。
(ps):对于分层图最短路,这并不是唯一的做法,可以进行(dp),不过博主太菜,没有看懂,有兴趣可以看(OI;Wiki)

(T2).

题目链接:
P4822 [BJWC2012]冻结
传说中爆搜能过,不过正解是分层图最短路。
我们类比(T1),发现每层图的连接边权值赋为原权值的一半即可。

(Code):

#include<iostream>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
const int MAXN=2555;
const int inf=2147483647;
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >q;
int dis[MAXN],vis[MAXN]={0};
int n,m,k,s,t;
int l,r,g;
struct node
{
	int to,nxt,w;
}e[201005];
int head[MAXN],cnt=0;
void add(int u,int v,int c)
{
	e[++cnt].to=v;
	e[cnt].nxt=head[u];
	e[cnt].w=c;
	head[u]=cnt;
}
void dij()
{
	while(!q.empty())
	{
		int g=q.top().second;
		q.pop();
		if(vis[g]) continue;
		vis[g]=1;
		for(int i=head[g];i;i=e[i].nxt)
		{
			int j=e[i].to;
			if(dis[j]>dis[g]+e[i].w)
			{
				dis[j]=dis[g]+e[i].w;
				q.push(make_pair(dis[j],j));
			}
		}
	}
	return;
}
int main()
{
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&l,&r,&g);
		add(l,r,g),add(r,l,g); 
		for(int j=1;j<=k;j++)
		{
			add(n*(j-1)+l,n*j+r,g/2);
			add(n*(j-1)+r,n*j+l,g/2);
			add(n*j+l,n*j+r,g);
			add(n*j+r,n*j+l,g);
		}
	}
	for(int i=1;i<=k;i++) add(n*i,n*(i+1),0);
	q.push(make_pair(0,1));
	for(int i=1;i<=n*(k+1);i++) dis[i]=inf; 
	dis[1]=0;
	dij();
	printf("%d
",dis[(k+1)*n]);
	return 0;
}

(T3).

题目链接:P2939 [USACO09FEB]Revamping Trails G
双倍经验?!
(T1)是一样的,只不过数组忘了(+k)炸了(n)次。

(Code):

#include<iostream>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
const int MAXN=220005;
const int inf=2147483647;
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >q;
int dis[MAXN];
int vis[MAXN]={0};
int n,m,k,s,t;
int l,r,g;
struct node
{
	int to,nxt,w;
}e[4100055];
int head[MAXN],cnt=0;
void add(int u,int v,int c)
{
	e[++cnt].to=v;
	e[cnt].nxt=head[u];
	e[cnt].w=c;
	head[u]=cnt;
}
void dij()
{
	while(!q.empty())
	{
		int g=q.top().second;
		q.pop();
		if(vis[g]) continue;
		vis[g]=1;
		for(int i=head[g];i;i=e[i].nxt)
		{
			int j=e[i].to;
			if(dis[j]>dis[g]+e[i].w)
			{
				dis[j]=dis[g]+e[i].w;
				q.push(make_pair(dis[j],j));
			}
		}
	}
	return;
}
int main()
{
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&l,&r,&g);
		add(l,r,g),add(r,l,g); 
		for(int j=1;j<=k;j++)
		{
			add(n*(j-1)+l,n*j+r,0);
			add(n*(j-1)+r,n*j+l,0);
			add(n*j+l,n*j+r,g);
			add(n*j+r,n*j+l,g);
		}
	}
	for(int i=1;i<=k;i++) add(n*i,n*(i+1),0);
	q.push(make_pair(0,1));
	for(int i=1;i<=n*(k+1);i++) dis[i]=inf; 
	dis[1]=0;
	dij();
	printf("%d
",dis[(k+1)*n]);
	return 0;
}
原文地址:https://www.cnblogs.com/tlx-blog/p/12508398.html