题目链接
题意
给定n个点m个带权双向道路,这n个点各自有一个叫层级的属性。相邻层级间的点可以通过花费c转移。求1-n最短路。
思路
这题可能是kuangbin这个专题里难度数一数二的了。思路很清晰,裸的最短路,难点在于建图。这个建图又和POJ - 2502里专门恶心人的建图不一样,是真的需要动脑子的。
关于层级,虽然层级范围是1-n,但没有告诉你一定是1-n全出现,也没有告诉你一个层级只有一个点。这意味着:
- 离线处理变得困难
- 只能连接相邻的两层,比如不加处理直接连接1-3层级,如果2层级不存在,那么这条路线是不可行的。
最容易想到的方法应该是两层for,但是n范围是1e5,肯定会t。这里我们选择为每一个层级建造一个层节点,有点类似于超级源点超级汇点。在线处理每一个节点,将这个节点所属的层节点向节点连单向边(如果双向,那么同一层级所有点相互移动的花费是0,显然会wa),之后再将这个节点向上下层级节点连接单向边。对于m条边正常连接,之后跑最短路即可
下附一张样例二图片,节点456代表层级123的层节点
代码
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
using namespace std;
typedef long long ll;
typedef pair <int,int> P;
const int maxn=200500;
const int maxe=800500;//开大点没坏处 ~ ~
const int inf=0x3f3f3f3f;
int head[maxn];
struct Edge{
int to;
int next;
int w;
} edge[maxe];
int cnt;
int n,m,c;//点,边,跳跃花费
int dis[maxn];
void init(){
cnt=0;
memset(head,-1,sizeof(head));
return ;
}
inline void add(int u,int v,int w){
edge[cnt].next=head[u];
edge[cnt].to=v;
edge[cnt].w=w;
head[u]=cnt;
cnt++;
}
void dij(int start){
memset(dis,0x3f,sizeof(dis));
priority_queue<P,vector<P>,greater<P> > q;
dis[start]=0;
q.push(P(0,start));
while(!q.empty()){
P p=q.top(); q.pop();
int v=p.second;
if(dis[v]<p.first) continue;
for(int i=head[v];i!=-1;i=edge[i].next){
int tmp=edge[i].to;
if(dis[tmp]>dis[v]+edge[i].w){
dis[tmp]=dis[v]+edge[i].w;
q.push(P(dis[tmp],tmp));
}
}
}
return ;
}
int main(){
IOS
int tn;
cin>>tn;
for(int _=1;_<=tn;_++){
init();
cin>>n>>m>>c;
for(int i=1;i<=n;i++){
int l;
cin>>l;
add(l+n,i,0);//第l层层节点向i节点连单向边
if(l<n)
add(i,l+n+1,c);//i节点向l+1层连单向边
if(l>1)
add(i,l+n-1,c);//i节点向l-1层连单向边
}
while(m--){
int u,v,w;
cin>>u>>v>>w;
add(u,v,w);
add(v,u,w);
}
dij(1);
int ans=dis[n];
if(ans==inf)
ans=-1;
cout<<"Case #"<<_<<": "<<ans<<endl;
}
return 0;
}