zoukankan      html  css  js  c++  java
  • 题解——逃离僵尸岛(BFS+最短路+虚拟节点)

    题解——逃离僵尸岛(BFS+最短路+虚拟节点)

    *一道很巧妙的最短路问题,细节也要注意 *


    题面

    Description
    小a住的国家被僵尸侵略了!小a打算逃离到该国唯一的国际空港逃出这个国家。

    该国有N个城市,城市之间有道路相连。一共有M条双向道路。保证没有自环和重边。

    K个城市已经被僵尸控制了,如果贸然闯入就会被感染TAT...所以不能进入。由其中任意城市经过不超过S条道路就可以到达的别的城市,就是危险城市。换句话说只要某个没有被占城市到某个被占城市不超过s距离,就是危险。

    小a住在1号城市,国际空港在N号城市,这两座城市没有被侵略。小a走每一段道路(从一个城市直接到达另外一个城市)得花一整个白天,所以晚上要住旅店。安全的的城市旅馆比较便宜要P元,而被危险的城市,旅馆要进行安保措施,所以会变贵,为Q元。所有危险的城市的住宿价格一样,安全的城市也是。在1号城市和N城市,不需要住店。

    小a比较抠门,所以他希望知道从1号城市到N号城市所需要的最小花费。

    输入数据保证存在路径,可以成功逃离。输入数据保证他可以逃离成功。

    Input
    第一行4个整数(N,M,K,S)
    第二行2个整数(P,Q)
    接下来K行,ci,表示僵尸侵占的城市
    接下来M行,ai,bi,表示一条无向边

    Output
    一个整数表示最低花费

    in.1
    13 21 1 1
    1000 6000
    7
    1 2
    3 7
    2 4
    5 8
    8 9
    2 5
    3 4
    4 7
    9 10
    10 11
    5 9
    7 12
    3 6
    4 5
    1 3
    11 12
    6 7
    8 11
    6 13
    7 8
    12 13

    out.1
    11000

    数据范围与约定
    对于20%数据,N<=50
    对于100%数据,2 ≦ N ≦ 100000, 1 ≦ M ≦ 200000, 0 ≦ K ≦ N - 2, 0 ≦ S ≦ 100000
    1 ≦ P < Q ≦ 100000

    思路

    主要分为两部分:

    1.处理出哪些点是危险节点,哪些点是安全节点。
    2.点权下放为边权

    处理:
    1.显然不可能对每一个僵尸围城的点做一次BFS,显然超时,原因就是有重复点被多次遍历,如果我们仅用vis数组判重,又会截断点之间的深度deep关系(可能有更加浅的点来更新这个点)。
    所以,我们建立一个虚拟源点,向所有僵尸围城的点连单向边。(由于ssw02只建一个图,所以单向边,绝对不能返回 ),这样可以较好地保持点之间的深度deep关系。

    2.这道题很特殊,一条单向边的边权之和这条边的目的地有关,我们只要处理问题一,把每一条边抽出来加边权即可。

    细节:
    1.初始用虚拟源点时只能连单向边。
    2.如果只用一个图,注意初始边权为0,后面加边权。
    3.加边权时注意优先级!!
    4.加边权时,碰到僵尸围城的点,把两条边都赋值为inf (严格意义下加指向其的单边也行)
    5.边的数量会因为虚拟的边而增多 100000 条 单向边(这可以不乘2)

    AC code: 我写的相当简洁

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long 
    const int MAXN = 100005 , MAXM = 200005 + 100000 ;//多加的边数 
    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 , Q1 , Q2 , head[ MAXN ] , danger[ MAXN ] , to[ MAXM*2 ] , nex[ MAXM*2 ]  , tot = 1 ; 
    ll dis[ MAXN ] , w[ MAXM*2 ] ;
    bool vis[ MAXN ]  ;
    priority_queue< pair<ll,int> >q ;
    queue< pair<int,int> >ql ;
    void  add( int  x , int  y , int  z ){
    	to[ ++tot ] = y , nex[ tot ] = head[ x ] ,w[ tot ] = z , head[ x ] = tot ;  
    }
    void  bfs(){
    	ql.push( make_pair(0,0) ) ;
    	vis[ 0 ] = true ;
    	while( !ql.empty() ){
    		int  u = ql.front().first , dep = ql.front().second ; ql.pop() ;
    		for( int i = head[ u ] ; i ; i = nex[ i ] ){
    			if( vis[ to[ i ] ] || dep > S )continue ;
    			ql.push( make_pair( to[ i ] , dep+1 ) ) ;
    			vis[ to[i] ] = true , danger[ to[ i ] ] = max( danger[ to[i] ] , 1 ) ;
    		}
    	}
    }
    void  dijkstra(){
    	for( int i = 1 ; i <= N ; ++i )dis[ i ] = 20000000005 ;//题意所得最大 20000000000
    	dis[ 1 ] = 0 ;
    	q.push( make_pair( 0LL,1 ) ) ;
    	while( !q.empty() ){
    		int  u = q.top().second ; q.pop() ;
    		if( vis[ u ] )continue ;
    		vis[ u ] = true ;
    		for( register int i = head[ u ] ; i ; i = nex[ i ] ){
    			if( dis[ to[ i ] ] > dis[ u ] + w[ i ] ){
    				dis[ to[ i ] ] = dis[ u ] + w[ i ] ;
    				q.push( make_pair( -dis[ to[ i ] ] , to[ i ] ) ) ;
    			}
    		}
    	}
    }
    int main(){
    	N = read() , M = read() , K = read() , S = read() , Q1 = read() , Q2 = read() ;
    	int m1 , m2 ;
    	for( int i = 1 ; i <= K ; ++i ){
    		m1 = read() , danger[ m1 ] = 2 , add( 0 , m1 , 0 ) ;//单向 
    	}
    	if( K%2 )tot++ ; K = tot ;
    	for( int i = 1 ; i <= M ; ++i ){
    		m1 = read() , m2 = read() ;
    		add( m1 , m2 , 0 ) ; add( m2 , m1 , 0 ) ;
    	}
    	bfs() ;
    	memset( vis , false , sizeof(vis) ) ;
    	for( int i = K ; i <= tot ; ++i ){//点权下放,分配边权 
    	    if( w[ i ] )continue ;
    	    if( danger[ to[i] ] == 2 ){w[ i ] = w[ i^1 ] = 20000000005 ;}
    		else if( danger[ to[i] ] == 1 )w[ i ] = Q2 ;
    	        else w[ i ] = Q1 ;
    	    w[ i ] = ( to[ i ]  == 1 || to[ i ] == N )? 0 : w[ i ] ; 
    	}
    	dijkstra() ;
    	cout<<dis[ N ] ;
    	return 0 ;
    }
    

    ssw02 的得分 42 -> 28 -> 57 -> 100 真是什么情况ssw02都撞上了

    如有不足,请大佬指出

  • 相关阅读:
    PYTHON压平嵌套列表
    linux下IPTABLES配置详解
    Python面试必须要看的15个问题
    两个实用的Python的装饰器
    Python的16个“坑”
    python实现不可修改的常量
    51nod-1322: 关于树的函数
    51nod-1310: Chandrima and XOR
    51nod-1296: 有限制的排列
    51nod-1277: 字符串中的最大值
  • 原文地址:https://www.cnblogs.com/ssw02/p/11402714.html
Copyright © 2011-2022 走看看