【HDU】4725 The Shortest Path in Nya Graph 最短路

传送门:【HDU】4725 The Shortest Path in Nya Graph


题目大意:给你一个平面无向图,N个点,M条边。每条边都有一个边权cost,表示从这条边经过需要花费cost的代价。并且图中每个节点有一个等级,其中从等级x的点可以到达等级x+1或x-1的任意一个点,但是需要花费C。比方存在边(u,v),并且u的等级和v的等级相差1,那么从u到v可以选择花费cost到达也可以选择花费C到达。现在你的任务是找到一条从1到N的花费最小的路,输出花费,无解输出-1。


题目分析:

这题用到了一种思想,也是比较通用的思想:

将有影响的元素额外拉出来,然后按照关系建边。

就拿本题来说,每个节点有一个等级,那么我们为什么不将等级拉出来额外考虑呢?

对于每个节点u,他的等级为x,如果等级x+1存在,就建边(u,n + x + 1,C),如果等级x-1存在同理。

并且对于每个节点u,他的等级为x,那么建边(n + x,u,0)。

这样子处理以后,我们就成功将题目转化成求一次1到N的最短路即可。

虽然处理出来了,但是如果建边不当,依旧会TLE。

我们仔细观察可以发现,除了m条边必须双向外,其他的边单向足以。那么就有很多多余的边就可以剔除了。


因为这次我用的SPFA,对于本题,如果不加输入优化或者SPFA的SLF优化,那么G++ TLE,C++ 600+ms,

加了输入优化或者SLF优化G++ 500~600+ms

两个都加,可以成功优化到218ms。。。

不过能卡掉我不优化的代码可以说可能是我写的比较搓把。。。


代码如下:


#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )
#define REPF( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define REPV( i , a , b ) for ( int i = a ; i >= b ; -- i )
#define clear( a , x ) memset ( a , x , sizeof a )

const int MAXN = 100005 ;
const int MAXQ = 200005 ;
const int MAXE = 1000005 ;
const int INF = 0x3f3f3f3f ;

struct Edge {
	int v , c , n ;
	Edge () {}
	Edge ( int var , int cost , int next ) :
		v ( var ) , c ( cost ) , n ( next ) {}
} ;

Edge E[MAXE] ;
int hd[MAXN << 1] , cntE ;
int d[MAXN << 1] ;
int LV[MAXN] , vis[MAXN] ;
int Q[MAXQ] , head , tail ;
bool inq[MAXN << 1] ;
int NV , NE , C ;

void addedge ( int u , int v , int c ) {
	E[cntE] = Edge ( v , c , hd[u] ) ;
	hd[u] = cntE ++ ;
}

void read ( int &x ) {
	x = 0 ;
	char c = ' ' ;
	while ( c < '0' || c > '9' )
		c = getchar () ;
	while ( c >= '0' && c <= '9' ) {
		x = x * 10 + c - '0' ;
		c = getchar () ;
	}
}

void spfa () {
	clear ( d , INF ) ;
	clear ( inq , 0 ) ;
	head = tail = 0 ;
	d[1] = 0 ;
	Q[tail ++] = 1 ;
	while ( head != tail ) {
		int u = Q[head ++] ;
		if ( head == MAXQ )
			head = 0 ;
		inq[u] = 0 ;
		for ( int i = hd[u] ; ~i ; i = E[i].n ) {
			int v = E[i].v , val = d[u] + E[i].c ;
			if ( d[v] > val ) {
				d[v] = val ;
				if ( !inq[v] ) {
					inq[v] = 1 ;
					if ( d[v] < d[Q[head]] ) {
						Q[-- head] = v ;
						if ( head == 0 )
							head = MAXQ - 1 ;
					}
					else {
						Q[tail ++] = v ;
						if ( tail == MAXQ )
							tail = 0 ;
					}
				}
			}
		}
	}
}
	

void work () {
	int u , v , c ;

	cntE = 0 ;
	clear ( hd , -1 ) ;
	clear ( vis , 0 ) ;
	read ( NV ) , read ( NE ) , read ( C ) ;
	REPF ( i , 1 , NV ) {
		read ( LV[i] ) ;
		addedge ( NV + LV[i] , i , 0 ) ;
		vis[LV[i]] = 1 ;
	}
	REPF ( i , 1 , NV ) {
		if ( vis[LV[i] + 1] )
			addedge ( i , NV + LV[i] + 1 , C ) ;
		if ( vis[LV[i] - 1] )
			addedge ( i , NV + LV[i] - 1 , C ) ;
	}
	REP ( i , NE ) {
		read ( u ) , read ( v ) , read ( c ) ;
		addedge ( u , v , c ) ;
		addedge ( v , u , c ) ;
	}
	spfa () ;
	if ( d[NV] == INF )
		printf ( "-1\n" ) ;
	else
		printf ( "%d\n" , d[NV] ) ;
}
			
int main () {
	int T , cas ;
	for ( read ( T ) , cas = 1 ; cas <= T ; ++ cas ) {
		printf ( "Case #%d: " , cas ) ;
		work () ;
	}
	return 0 ;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值