题意:leader在1号位置,他要用最小的花费把他的命令通知到位。
通知方式:每个人可以向离自己[L,R]的距离的人打电话(左右都可以),花费是C
如题:他花费1可以通知第三四人,三又花费1通知第二个人,第五个人没办法收到
所以通知五个人的最小花费分别是 0 2 1 1 -1
http://acm.hdu.edu.cn/showproblem.php?pid=5361
分析:
明显的最短路,但是边太多了,所以不能直接建边跑最短路。
用一个set存储所有的点,如果一个点有求得了最小花费,让这个点出set。
因为人固定了,他打电话的花费是固定的,所以可以用类似bfs的思想层次搜索。
第一个人能搜索的人入setF(setF按照花费从小到大排序),每次出set就是出的最小花费的点,根据此点继续进行搜索(在set里面二分找到可以搜的点),搜索到的点入setF。要知道根据这样的搜索方式,先搜索到的点一定已达到最小花费,所以改点直接出set,表示不会继续用它了。
setF表示要搜索的点(代码中的f),set表示点集(搜完就可以出set了,代码中的st)。
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<set>
#define sf(n) scanf("%d", &n)
#define MT(x,i) memset(x,i,sizeof(x))
typedef long long LL;
using namespace std;
const int MAXN=2*100005;
struct node
{
int id; LL dist;
node(int id=-1,LL dist=-1):dist(dist),id(id) {}
bool operator < (const node& rhs) const{ //这个重载< 必须要,让 搜索setF 按照花费从小到大
if(dist==rhs.dist) return id<rhs.id;
return dist<rhs.dist;
}
};
set<node>f; set<int>st;
int L[MAXN],R[MAXN],C[MAXN]; LL ans[MAXN];
void add(int start,int en,node cur)
{
set<int>::iterator s1=st.lower_bound(start);//二分找到可以开始的点
for(; s1!=st.end()&&*s1<=en;){
node y; y.id=*s1; st.erase(s1++); //出点集,以后不再用它
ans[y.id]=cur.dist; //更新最小花费,每个点只会更新这一次
y.dist=cur.dist+C[y.id]; //花费需要加上自己的花费
f.insert(y); //改点入搜索set
}
}
void solve()
{
while(!f.empty()) {
node cur=*f.begin(); f.erase(f.begin());//正在搜索的点
int x=cur.id,start=x+L[x],en=x+R[x];
add(start,en,cur);//搜索左边可以到达的点
start=x-R[x],en=x-L[x];
add(start,en,cur);//搜索右边可以到达的点
}
}
int main()
{
int T,n; sf(T);
while(T--){
sf(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("%d",&C[i]);
f.clear(); st.clear();
for(int i=1; i<n; i++) st.insert(i);
f.insert(node(0,C[0]));//编号,距离
MT(ans,-1); ans[0]=0;
//以上初始化
solve();
for(int i=0; i<n; i++)
printf("%I64d%s",ans[i],i==(n-1)?"\n":" ");
}
return 0;
}