题目链接:
题目大意:
给出以一些在同一直线上的点,其中每个点i可以走到与它之间的距离在 [li,ri] 区间的点,每次传送的花费是 ci ,那么问从0到达任意一点的最小花费。
题目分析:
- 首先因为起点固定,且到达每个点的花费都为正数,我们维护一个set,储存一个点,排序的权值是它的最优解+到下一点的花费,其中的越早到达set的顶部,会先从集合中取出的点的花费一定更小,所以某个点早到达一定会比晚到达获得更优的解。然后已经得到最优解的点放入set,然后继续更新,直到所有的点都被更新。
- 两个集合set1,set2,一个集合用来存储id,一个用来存当前的权值最小的集合。
AC代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#define MAX 200007
using namespace std;
typedef long long LL;
struct State
{
int id;
LL cost;
State (){}
State ( int a , LL b )
{
id = a;
cost = b;
}
bool operator < ( const State& a ) const
{
if ( cost == a.cost ) return id < a.id;
return cost < a.cost;
}
};
int t,n;
int l[MAX],r[MAX];
LL c[MAX];
set<int> point;
set<State> state;
LL dis[MAX];
int main ( )
{
scanf ( "%d" , &t );
while ( t-- )
{
state.clear();
point.clear();
scanf ("%d" , &n );
for ( int i = 0 ; i < n ; i++ )
scanf ( "%d" , &l[i] );
for ( int i = 0 ; i < n ; i++ )
scanf ( "%d" , &r[i] );
for ( int i = 0 ; i < n ; i++ )
scanf ( "%I64d" , &c[i] );
memset ( dis , -1 , sizeof ( dis ));
dis[0] = 0;
state.insert ( State ( 0 , c[0] ) );
for ( int i = 1 ; i < n ; i++ )
point.insert ( i );
set<int>::iterator it1,it2;
while ( !state.empty() )
{
State top = *(state.begin());
int id = top.id;
LL cost = top.cost;
state.erase ( state.begin());
it1 = point.lower_bound ( id- r[id] );
while ( it1 != point.end() && *it1 <= id-l[id] )
{
State temp;
temp.id = *it1;
temp.cost = c[*it1]+cost;
state.insert ( temp );
dis[*it1] = cost;
it2 = it1++;
point.erase ( it2 );
}
it1 = point.lower_bound ( id + l[id] );
while ( it1 != point.end() && *it1 <= id+r[id] )
{
State temp;
temp.id = *it1;
temp.cost = c[*it1]+cost;
state.insert ( temp );
dis[*it1] = cost;
it2 = it1++;
point.erase ( it2 );
}
}
for ( int i = 0 ; i < n ; i++ )
{
if ( i ) printf ( " ");
printf ( "%I64d" , dis[i] );
}
puts ( "" );
}
}