zoukankan      html  css  js  c++  java
  • 学习笔记:分层图(主要是最短路)

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

    引入

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


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

    分层图最短路

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

    一般模型

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

    (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;
    }
    
  • 相关阅读:
    Mysql多实例配置
    Mysql多实例主从复制
    粪发涂墙-321
    粪发涂墙-123
    SpringCloud-粪发涂墙90
    线上BUG定位神器(阿尔萨斯)-Arthas2019-0801
    confluence-工具安装
    新应用启动之类冲突-2019-7-26
    新项目组之应用启动-2019-07-25
    新装虚拟机-2019-07-24日记
  • 原文地址:https://www.cnblogs.com/tlx-blog/p/12508398.html
Copyright © 2011-2022 走看看