分层图最短路大礼包

分层图最短路大礼包

*今天模拟赛,ssw02没看出来T2裸的分层图,同时T3网络流还打错了,艹 *
快CSP了,还是赶快写写总结,补一下分层图最短路的坑吧

欢迎转载ssw02的博客:https://www.cnblogs.com/ssw02/p/11656868.html


[JLOI2011]飞行路线

题面:

Alice和Bob现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在n个城市设有业务,设这些城市分别标记为0到n-1,一共有m种航线,每种航线连接两个城市,并且航线有一定的价格。

Alice和Bob现在要从一个城市沿着航线到达另一个城市,途中可以进行转机。航空公司对他们这次旅行也推出优惠,他们可以免费在最多k种航线上搭乘飞机。那么Alice和Bob这次出行最少花费多少?

思路:

考虑到 K 非常的小,那么我们在普通最短路上将 dis[i] 改为 dis[i][j] 表示在第 i 个点,免费次数使用 j 次的最优值。

主要的dijkstra函数也要改一下

for( int i = head[u] ; i ; i = nex[i] ){
	if( dis[to[i]][stp] > dis[u][stp] + w[i] ){
		dis[to[i]][stp] = dis[u][stp] + w[i] ;
		q.push( make_pair( -dis[ to[i] ][stp] , (node){ to[i] , stp } ) ) ;
	}
	if( dis[to[i]][stp+1] > dis[u][stp] && stp < K ){
		dis[to[i]][stp+1] = dis[u][stp] ;
		q.push( make_pair( -dis[ to[i] ][stp+1] , (node){ to[i] , stp+1 } ) ) ;
	}
} 

这个思想已经有点类似DP,因为不存在一次使用2次免费机会的情况。

代码:

#include<bits/stdc++.h>
using namespace std ;
#define ll long long
const int MAXN = 10005 , MAXM = 50005 ;
inline int read(){
	int s=0 ;char g=getchar() ; while(g>'9'||g<'0')g=getchar() ;
	while( g>='0'&&g<='9')s=s*10+g-'0',g=getchar() ; return s ;
}
int N , M , K , S , T , dis[ MAXN ][ 11 ] ;
bool vis[ MAXN ][ 11 ] ;  
int head[ MAXN ] , nex[ MAXM*2 ] , to[ MAXM*2 ] , w[ MAXM*2 ] , tot = 1 ;
struct node{
	int u , k ;
};
bool operator <( const node &x , const node &y ){//优先队列必须重载运算符 
	return x.k < y.k ;
}
priority_queue< pair<int,node> >q ;
void  add( int x , int y , int z ){
	to[ ++tot ] = y , nex[tot] = head[x] , head[x] = tot , w[tot] = z ;
}
void  dijkstra(){
	for( int i = 1 ; i <= N ; ++i )
	    for( int j = 0 ; j <= 10 ; ++j )dis[i][j] = (1<<30) ;
	dis[S][0] = 0 ;
	q.push( make_pair( 0 , (node){ S , 0 } ) ) ;
	while( !q.empty() ){
		node now = q.top().second ; q.pop() ;
		int u = now.u , stp = now.k ; 
		if( vis[u][stp] )continue ; vis[u][stp] = true ;
		for( int i = head[u] ; i ; i = nex[i] ){
			if( dis[to[i]][stp] > dis[u][stp] + w[i] ){
				dis[to[i]][stp] = dis[u][stp] + w[i] ;
				q.push( make_pair( -dis[ to[i] ][stp] , (node){ to[i] , stp } ) ) ;
			}
			if( dis[to[i]][stp+1] > dis[u][stp] && stp < K ){
				dis[to[i]][stp+1] = dis[u][stp] ;
				q.push( make_pair( -dis[ to[i] ][stp+1] , (node){ to[i] , stp+1 } ) ) ;
			}
		} 
	}
}
int main(){
	N = read() , M = read() , K = read() , S = read()+1 , T = read()+1 ; int m1 , m2 , m3 ;
	for( int i = 1 ; i <= M ; ++ i){
		m1 = read()+1 , m2 = read()+1 , m3 = read() ;
		add( m1 , m2 , m3 ) , add( m2 , m1 , m3 ) ; 
	}
	dijkstra();
	int ans = (1<<30) ;
	for( int i = 0 ; i <= K ; ++i )ans = min( ans , dis[T][i] ) ;
	cout<<ans ;
	return 0 ; 
}

[BJWC2012]冻结

和上面一道几乎一样,稍微改改就行了

#include<bits/stdc++.h>
using namespace std ;
#define ll long long
const int MAXN = 51 , MAXM = 1005 ;
inline int read(){
	int s=0 ;char g=getchar() ; while(g>'9'||g<'0')g=getchar() ;
	while( g>='0'&&g<='9')s=s*10+g-'0',g=getchar() ; return s ;
}
int N , M , K , S , T ;
ll dis[ MAXN ][ 51 ] ;
bool vis[ MAXN ][ 51 ] ;  
int head[ MAXN ] , nex[ MAXM*2 ] , to[ MAXM*2 ] , w[ MAXM*2 ] , tot = 1 ;
struct node{
	int u , k ;
};
bool operator <( const node &x , const node &y ){//优先队列必须重载运算符 
	return x.k < y.k ;
}
priority_queue< pair<ll,node> >q ;
void  add( int x , int y , int z ){
	to[ ++tot ] = y , nex[tot] = head[x] , head[x] = tot , w[tot] = z ;
}
void  dijkstra(){
	for( int i = 1 ; i <= N ; ++i )
	    for( int j = 0 ; j <= K ; ++j )dis[i][j] = (1LL<<60) ;
	dis[1][0] = 0 ;
	q.push( make_pair( 0 , (node){ 1 , 0 } ) ) ;
	while( !q.empty() ){
		node now = q.top().second ; q.pop() ;
		int u = now.u , stp = now.k ; 
		if( vis[u][stp] )continue ; vis[u][stp] = true ;
		for( int i = head[u] ; i ; i = nex[i] ){
			if( dis[to[i]][stp] > dis[u][stp] + (ll)w[i] ){
				dis[to[i]][stp] = dis[u][stp] + (ll)w[i] ;
				q.push( make_pair( -dis[ to[i] ][stp] , (node){ to[i] , stp } ) ) ;
			}
			if( dis[to[i]][stp+1] > dis[u][stp]+(ll)w[i]/2 && stp < K ){
				dis[to[i]][stp+1] = dis[u][stp]+(ll)w[i]/2 ;
				q.push( make_pair( -dis[ to[i] ][stp+1] , (node){ to[i] , stp+1 } ) ) ;
			}
		} 
	}
}
int main(){
	N = read() , M = read() , K = read()  ; int m1 , m2 , m3 ;
	for( int i = 1 ; i <= M ; ++ i){
		m1 = read() , m2 = read() , m3 = read() ;
		add( m1 , m2 , m3 ) , add( m2 , m1 , m3 ) ; 
	}
	dijkstra();
	ll ans = (1LL<<60) ;
	for( int i = 0 ; i <= K ; ++i )ans = min( (ll)ans , dis[N][i] ) ;
	cout<<ans ;
	return 0 ; 
}

[SHOI2012]回家的路

题面:

2046 年 OI 城的城市轨道交通建设终于全部竣工,由于前期规划周密,建成后的轨道交通网络由2n2n条地铁线路构成,组成了一个nn纵nn横的交通网。如下图所示,这2n2n条线路每条线路都包含nn个车站,而每个车站都在一组纵横线路的交汇处。

出于建设成本的考虑,并非每个车站都能够进行站内换乘,能够进行站内换乘的地铁站共有mm个,在下图中,标上方块标记的车站为换乘车站。已知地铁运行 1 站需要 2 分钟,而站内换乘需要步行 1 分钟。Serenade 想要知道,在不中途出站的前提下,他从学校回家最快需要多少时间(等车时间忽略不计)。

思路:

这道题我们考虑到2个问题。

问题1 : 换乘的问题,我们只要把每一个点裂点,分为横着的一层和竖着的一层,然后在两层的同一节点连一条双向边即可

问题2 : 点数过多,我们考虑到,每一个点都只能和它同一行或者同一列的点连边,而且任意一条横跨车站的边都可以由其他两条边等效代替,而且只有换乘的点才用记录,这就解决了上述问题 。

细节:

数组大小一定要算好,本来就要裂点,还要多加层与层之间的边,还是双向边。。。

初始车站和终点车站可能不是可以换乘的车站

代码:

#include<bits/stdc++.h>
using namespace std ;
#define ll long long
const int MAXN = 100005*2 , MAXM = 100000*4+100005*2 ;
inline int read(){
	int s=0 ;char g=getchar() ; while(g>'9'||g<'0')g=getchar() ;
	while( g>='0'&&g<='9')s=s*10+g-'0',g=getchar() ; return s ;
}
int  N , M , cnt , s1 , s2 , t1 , t2 , S , T ;
int head[ MAXN ] , to[ MAXM ] , nex[ MAXM ] , w[ MAXM ] , tot = 1  ;
ll dis[ MAXN ] ;
bool vis[ MAXN ] ;
priority_queue< pair<ll,int> >q ;
void  add( int x , int y , int z ){
	to[ ++tot ] = y , nex[ tot ] = head[ x ] , head[ x ] = tot , w[ tot ] = z ;
	to[ ++tot ] = x , nex[ tot ] = head[ y ] , head[ y ] = tot , w[ tot ] = z ;
}
struct ap{
	int x , y , id1 , id2 ;
}t[ MAXN ] ;
bool cmp( ap a , ap b ){
	return (a.x==b.x)?(a.y<b.y):(a.x<b.x) ;
}
bool cmp2( ap a , ap b ){
	return (a.y==b.y)?(a.x<b.x):(a.y<b.y) ;
}
void  dijkstra(){

	for( int i = 1 ; i <= cnt ; ++i )dis[ i ] = (1LL<<60) ;
	dis[ S ] = 0 , dis[ S+1 ] = 0 ;
	q.push( make_pair( 0 , S ) ) , q.push( make_pair( 0 , S+1 ) ) ;
	while( !q.empty() ){
		int u = q.top().second ; q.pop() ;
		if( vis[ u ] )continue ; vis[ u ] = true ;
		for( int i = head[u] ; i ; i = nex[i] )
		    if( dis[ to[i] ] > dis[u] + (ll)w[i] ){
		    	dis[ to[i] ] = dis[u] + (ll)w[i] ;
		    	q.push( make_pair(-dis[ to[i] ] , to[ i ] ) ) ;
		    }
	}
}
int main(){
	N = read() , M = read() ;
	for( int i = 1 ; i <= M ; ++i ){
		t[i].x = read() , t[i].y = read() , t[i].id1 = ++cnt , t[i].id2 = ++cnt ;
		add( t[i].id1 , t[i].id2 , 1 ) ;
 	} 
 	s1 = read() , s2 = read() , t1 = read() , t2 = read() ;
 	t[M+1].x = s1 , t[M+1].y = s2 , t[M+1].id1 = ++cnt , t[M+1].id2 = ++cnt ; 
 	t[M+2].x = t1 , t[M+2].y = t2 , t[M+2].id1 = ++cnt , t[M+2].id2 = ++cnt ; 
	sort( t+1 , t+M+3 , cmp ) ;
	for( int i = 1 ; i <= M+2 ; ++i ){
		if( t[i].x == t[i+1].x )add( t[i].id1 , t[i+1].id1 , (t[i+1].y-t[i].y)*2 ) ;
		if( t[i].x == s1 && t[i].y == s2 )S = t[i].id1 ; 
		if( t[i].x == t1 && t[i].y == t2 )T = t[i].id1 ;
	}
		
	sort( t+1 , t+M+3 , cmp2 ) ;
	for( int i = 1 ; i <= M+2 ; ++i )
		if( t[i].y == t[i+1].y )add( t[i].id2 , t[i+1].id2 , (t[i+1].x-t[i].x)*2 ) ;
	dijkstra() ;
	cout<<min( dis[ T ] , dis[ T+1 ] ) ;
	return 0 ; 
}

拯救大兵瑞恩

题面:

特种兵麦克接到国防部的命令,要求立即赶赴太平洋上的一个孤岛,营救被敌军俘虏的大兵瑞恩。瑞恩被关押在一个迷宫里,迷宫地形复杂,但幸好麦克得到了迷宫的地形图。迷宫的外形是一个长方形,其南北方向被划分为 N 行,东西方向被划分为 M 列,于是整个迷宫被划分为 N×M 个单元。每一个单元的位置可用一个有序数对(单元的行号,单元的列号)来表示。南北或东西方向相邻的 2 个单元之间可能互通,也可能有一扇锁着的门,或者是一堵不可逾越的墙。迷宫中有一些单元存放着钥匙,并且所有的门被分成PP 类,打开同一类的门的钥匙相同,不同类门的钥匙不同。

大兵瑞恩被关押在迷宫的东南角,即 (N,M) 单元里,并已经昏迷。迷宫只有一个入口,在西北角。也就是说,麦克可以直接进入 (1,1) 单元。另外,麦克从一个单元移动到另一个相邻单元的时间为 1,拿取所在单元的钥匙的时间以及用钥匙开门的时间可忽略不计。

试设计一个算法,帮助麦克以最快的方式到达瑞恩所在单元,营救大兵瑞恩。

思路:

走迷宫问题的加强版。

钥匙为什么这么少?好像可以状压,嗯,是的。

我们考虑以每种钥匙的不同集合为不同的状态,可以分为多层。

墙壁和门如何解决? 用 mp[x][y][xx][yy] 表示( x,y )和( xx,yy )间是否有障碍。 -1表示墙 , 若是门 , 则用pow[]压缩后表示所需要的钥匙集合。

然后用 vis[x][y][key]表示在( x,y ),所持有钥匙集合为 key 的最短距离。跑bfs即可。

小技巧:

获得已有钥匙 key| keys[xx][yy]

表示是否可以开门 if( (mp[x][y][xx][yy] & key ) != mp[x][y][xx][yy] )

代码:

#include<bits/stdc++.h>
using namespace std ;
inline int read(){
	int s=0 ; char g=getchar() ; while(g>'9'||g<'0')g=getchar() ;
	while(g>='0'&&g<='9')s=s*10+g-'0',g=getchar() ; return s ;
}
int N , M , P , K , J , ans=-1 , to[4][2] = { {0,1},{0,-1},{1,0},{-1,0} };
int pw[15] , mp[12][12][12][12] , keys[12][12];
bool vis[12][12][16385] ;
struct node{
	int x , y , key , stp ;
};
queue<node>q ;
void  prepare(){
	pw[1] = 1 ;
	for( int i = 2 ; i <= 14 ; ++i )pw[i] = pw[i-1]<<1 ;
}
void  bfs(){
	vis[1][1][ keys[1][1] ] = true ;
	q.push( (node){ 1 , 1 , keys[1][1] , 0 } ) ;
	while( !q.empty() ){
		node u = q.front() ; q.pop() ;
		int x = u.x , y = u.y , key = u.key , stp = u.stp ;
		if( x == N && y == M ){ ans = stp ; return ; }
		for( int k = 0 ; k <= 3 ; ++k ){
			int xx = x+to[k][0] , yy = y+to[k][1] ;
			if( xx < 1 || xx > N || yy < 1 || yy > M )continue ;
			if( mp[x][y][xx][yy]==-1 )continue ;
			if( (mp[x][y][xx][yy] & key ) != mp[x][y][xx][yy] )continue ;
			if( vis[xx][yy][key] )continue ;//已访问,不优
			vis[xx][yy][key]=true;
			q.push( (node){ xx , yy , key| keys[xx][yy] , stp+1 } ) ;
		}
	}
}
int main(){
	prepare() ;//预处理 
	N = read() , M = read() , P = read() ;
	K = read() ; int m1 , m2 , m3 , m4 , m5 ;
	for( int i = 1 ; i <= K ; ++i ){
		m1=read(),m2=read(),m3=read(),m4=read(),m5=read();
		mp[m1][m2][m3][m4]=mp[m3][m4][m1][m2]=( m5?pw[m5]:-1 ) ;
	}
	J = read() ;
	for( int i = 1 ; i <= J ; ++i ){
		m1 = read() , m2 = read()  , m3 = read();
		keys[m1][m2] |= pw[m3] ;
	}
	bfs() ;
	cout<<ans ;
}

速度限制

在这个繁忙的社会中,我们往往不再去选择最短的道路,而是选择最快的路线。开车时每条道路的限速成为最关键的问题。不幸的是,有一些限速的标志丢失了,因此你无法得知应该开多快。一种可以辩解的解决方案是,按照原来的速度行驶。你的任务是计算两地间的最快路线。

你将获得一份现代化城市的道路交通信息。为了使问题简化,地图只包括路口和道路。每条道路是有向的,只连接了两条道路,并且最多只有一块限速标志,位于路的起点。两地A和B,最多只有一条道路从A连接到B。你可以假设加速能够在瞬间完成并且不会有交通堵塞等情况影响你。当然,你的车速不能超过当前的速度限制。

第一行是3个整数N,M和D(2<=N<=150),表示道路的数目,用0..N-1标记。M是道路的总数,D表示你的目的地。

接下来的M行,每行描述一条道路,每行有4个整数A(0≤A<N),B(0≤B<N),V(0≤V≤500)and L(1≤L≤500),这条路是从A到B的,速度限制是V,长度为L。如果V是0,表示这条路的限速未知。

如果V不为0,则经过该路的时间T=L/V。否则T=L/Vold,Vold是你到达该路口前的速度。开始时你位于0点,并且速度为70。

思路:

还是分层图。用 dis[i][j] 表示到i点,速度为j的最短距离

分情况讨论 2 种边即可

代码:

#include<bits/stdc++.h>
using namespace std ;
const int MAXN = 155 , MAXM = 151*151 ;
inline int read(){
	int s=0 ; char g=getchar() ; while(g>'9'||g<'0' )g=getchar() ; 
	while( g>='0'&&g<='9' )s=s*10+g-'0',g=getchar();return s ;
}
int N , M , T , S ;
int head[ MAXN ] , nex[ MAXM ] , to[ MAXM ] , len[ MAXM ] , speed[ MAXM ]  , tot = 1 ;
double dis[ MAXN ][ 505 ] ; 
bool vis[ MAXN ][ 505 ] ;
void  add( int x , int y , int z , int t ){
	to[ ++tot ]=y,speed[tot]=z,len[tot]=t,nex[tot]=head[x],head[x]=tot; 
}
struct node{int u ,v ; }pre[MAXN][505];
queue< pair<int,int> >q ;
void  SPFA(){
	q.push(make_pair( 1 , 70 ) ) ;
	for( int i = 1 ; i <= N+1 ; ++i )
	    for( int j = 0 ; j <= 500 ; ++j )
	        dis[i][j] = (1<<30) , pre[i][j].u = pre[i][j].v = -1 ;
	dis[1][70]=0 ,vis[1][70] = true ;
	while( !q.empty() ){
		int  u = q.front().first , spe = q.front().second ; q.pop();vis[u][spe]=false ;
		//cout<<u<<" ";
		for( int i = head[ u ] ; i ; i = nex[ i ] ){
			int v2 = speed[i] ? speed[i]:spe ;
			if( dis[u][spe]+(double)len[i]/(double)v2 < dis[to[i]][v2] ){
				dis[to[i]][v2]=dis[u][spe]+(double)len[i]/(double)v2 ;
				pre[to[i]][v2].u=u,pre[to[i]][v2].v=spe ;
				if( !vis[to[i]][v2] ){
					q.push(make_pair(to[i],v2) ) ;
					vis[ to[i] ][ v2 ] = true ;
				}
			}
		} 
	}
}
inline void print(int u,int v) {
    if (pre[u][v].u!=-1) print(pre[u][v].u,pre[u][v].v);
    printf("%d ",u-1);
}
int main(){
    int m1 , m2 , m3 , m4 ;
	N = read()+1 , M = read() , T = read()+1 , S = 1 ;
	for( int i = 1 ; i <= M ; ++i ){
		m1 = read()+1 , m2 = read()+1 , m3 = read() , m4 = read() ;
		add( m1 , m2 , m3 , m4 ) ;
	}
	SPFA() ;
	double mi = 110000000 ; int  sped ;
	for( int i = 0 ; i <= 500 ; ++i )
	    if( dis[ T ][ i ] < mi )mi =dis[ T ][ i ] , sped = i ;
	print( T , sped ) ;
	return 0 ;
} 

欢迎各位转载ssw02的博客:https://www.cnblogs.com/ssw02/p/11656868.html

posted @ 2019-10-11 21:03  蓝银杏-SSW  阅读(179)  评论(0编辑  收藏  举报
//结束