题目链接:点击打开链接
题目大意:给出一个n个数的序列,标号为1到n,对于第i个数,它可以移动到距离i为[ li,ri ]的位置,花费为c[i],输入三行,第一行l[i],第二行r[i],第三行c[i],现在问对于第一个数来说,它移动到第i个位置的最小花费。(1<=i<=n)
这是一个每个点可以移动到一段中任意一个点,并且花费一样,这样就不适用与已有的四种最短路,但是可以对dijkstra进行变形,dij是每次找到一个距离最小的节点,用这个节点去更新其它节点,我们可以用一个优先队列,每次找到一个点,用这个点去更新其他的点,这个点要满足 dis[i]+c[i]是最小的,那么这个点更新出来的点也就是最小的。
有优先队列维护dis+c的值,用set来找到一段中还没有更新的点,这样就可以防止会遍历到已经更新好的点,造成超时。
#include <cstdio> #include <cstring> #include <queue> #include <set> #include <algorithm> using namespace std ; #define LL __int64 #pragma comment(linker, "/STACK:102400000,102400000") struct node{ int id ; LL c ; bool operator < (node a) const { return c > a.c ; } } p , q ; priority_queue<node> que ; set<int> s ; set<int> ::iterator iter ; LL dis[200010] , c[200010] ; int l[200010] , r[200010] ; void f(int l,int r) { iter = s.lower_bound(l) ; while( iter != s.end() ) { if( *iter > r ) break ; dis[ *iter ] = p.c ; q.id = *iter ; q.c = p.c + c[ q.id ] ; que.push( q ) ; iter++ ; s.erase(q.id) ; } return ; } int main() { int t , n , i , x , y ; //freopen("1009.in","r",stdin) ; //freopen("111.out","w",stdout) ; scanf("%d", &t) ; while( t-- ) { while( !que.empty() ) que.pop() ; memset(dis,-1,sizeof(dis)) ; s.clear() ; scanf("%d", &n) ; for(i = 2 ; i <= n ; i++) s.insert(i) ; for(i = 1 ; i <= n ; i++) scanf("%d", &l[i]) ; for(i = 1 ; i <= n ; i++) scanf("%d", &r[i]) ; for(i = 1 ; i <= n ; i++) scanf("%I64d", &c[i]) ; dis[1] = 0 ; p.id = 1 ; p.c = c[1] ; que.push(p) ; while( !que.empty() && !s.empty() ) { p = que.top() ; que.pop() ; x = max(p.id - r[ p.id ],1) ; y = p.id - l[ p.id ] ; if( y >= 1 ) f(x,y) ; x = p.id + l[ p.id ] ; y = min(p.id + r[ p.id ],n) ; if( x <= n ) f(x,y) ; } for(i = 1 ; i < n ; i++) printf("%I64d ", dis[i]) ; printf("%I64d\n", dis[i]) ; } return 0 ; }
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-11-14 12:48:57