乌拉乌拉国有n个城市和m条道路,城市编号为1∼n。由于乌拉乌拉国每一个城市都在创城(创建文明城市),因此,城市之间的道路通行施行道路交通管制:
已知从城市ui到城市vi的道路,需要时间ti。但是一旦当道路管理员进入某条道路后,任何人在道路管理员未驶出该道路前不允许进入该道路。例如:道路管理员在第4时刻进入该道路,在路上需要花费3时,那么在第4∼6时刻不允许其他人进入改街道,只能第7时刻及其以后进入或者在第4时刻之前进入。
现在,计算鸭知道,道路管理员从0时刻出发,依次经过g个城市,计算鸭从时刻k出发,从城市a前往城市b。请问,计算鸭最少需要多长时间。
输入格式:
输入的第一行给出两个整数n,m——表示城市的数量和道路的数量。
输入的第二行给出四个整数a,b,k,g——a,b分别表示计算鸭的初始城市和目的城市;k表示计算鸭出发时刻;g表示道路管理员需要经过的城市数量。
输入的第三行给出g个整数xi——表示道路管理员需要经过的城市编号。
接下来m行,每行3个整数ui,vi,ti——表示从ui至vi需要用时ti
2≤n≤103
2≤m≤104
1≤a,b,ui,vi≤n
0≤k,g≤103
1≤ti≤103
输出格式:
输出一个整数——表示计算鸭从a城市到b城市的最短用时。
输入样例:
6 5
1 6 20 4
5 3 2 4
1 2 2
2 3 8
2 4 3
3 6 10
3 5 15
输出样例:
21
输入样例:
8 9
1 5 5 5
1 2 3 4 5
1 2 8
2 7 4
2 3 10
6 7 40
3 6 5
6 8 3
4 8 4
4 5 5
3 4 23
输出样例:
40
题目大意:
题目的大致意思为,有一个城管会巡查街道,他会从时间为0开始从他的巡查城市列表的第一个城市一直走到最后一个城市(从第一个城市直达到第二个城市,第二个城市直达到第三个城市,以此类推到最后一个城市),当城管在巡视城市的路上的这段时间,你不能进入这段巡查的路(你可以在他巡查前进入,这个时候他开始巡查这条路的时候并不会赶你出去),然后我们的主人公从k这个时间开始,从a城市走到b城市,问你他所需要的最短时间。
题目思路:
第一眼肯定能想到这就是一到最短路,但是他对你的路径添加了一些限制,这个时候就要思考一下这个限制怎么处理了(其实很简单)。因为他的限制只看我进入这条路的时间,所以我们只需要在跑最短路的时候,如果我进入这条路的时间在他的限制时间内时,我们只需要等到限制时间结束时再走就可以,这个时候我的进入时间就被拖到了限制时间结束的时间+1.其实到这题目就已经解决了,我们只需要提前把从一个城市到另一个城市的路的限制时间段算出来就行(如果你不知道城管怎么走的,其实路就在那,你主人公和城管走的路是一样的,所以城管走的路也就是题目给的路,而且题目没给走不通路的情况输出什么,所以就是变相保证了城管走的路必然存在,并且你肯定能从a城市走到b城市)
还需要注意的是边的设置:
如果是单相边的话,样例都出不来()
所以我们可以理算应当的推断为他是双向边()
然后时间刻度的开始时间也算一秒,就和题目描述的一样:
道路管理员在第4时刻进入该道路,在路上需要花费3时,那么在第4∼6时刻不允许其他人进入改街道
意思就是我在a秒进入这条路,我走过这条路的时间为k秒,我的结束时间其实是a+k-1,而不是a+k
AC代码:
#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
#define PII pair<int,int>
int n,m,a,b,k,g;
int de[1005],mp[1005][1005],l[1005][1005],r[1005][1005];
//de为城管走的路径数组,mp为邻接表(因为查询容易)
//l和 r为从u(第一维下标) 到 v(第二维下标)时禁止通行的时间段
int h[1005],e[20005],ne[20005],w[20005],idx;
//链式前向星存边,为了djstl算法准备
int dist[1005];
bool vis[1005];
void add(int u,int v,int c){//链式前向星加边
ne[idx]=h[u],e[idx]=v,w[idx]=c,h[u]=idx++;
}
void djstl(){//基本和模板差不多
memset(dist,0x3f,sizeof dist);
memset(vis,0,sizeof vis);
priority_queue<PII,vector<PII>,greater<PII>>q;
dist[a]=k-1;//因为从k开始,第k秒也算在内,所以当作k-1秒开始算
q.push({k-1,a});
while(!q.empty()){
int x=q.top().second;
q.pop();
if(vis[x])continue;
vis[x]=1;
for(int i=h[x];~i;i=ne[i]){
int y=e[i],now=dist[x];//now为我现在从x到y的开始时间
if(l[x][y]<=now&&now<=r[x][y]){
now=r[x][y]+1;//如果开始时间在x 到y 的禁止时间内
//我们需要等到禁止时间结束后再走此路,所以开始时间变成了r[x][y]+1
}
if(now+w[i]<dist[y]){//从正确的开始时间往后推
dist[y]=now+w[i];
q.push({dist[y],y});
}
}
}
}
int main(){
memset(h,-1,sizeof h);
memset(l,-1,sizeof l);
memset(r,-1,sizeof r);
idx=0;
//初始化
cin >> n >> m;
cin >> a >> b >> k >> g;
for(int i=0;i<g;i++)cin >> de[i];
for(int i=0;i<m;i++){
int u,v,c;cin >> u >> v >> c;
add(u,v,c);add(v,u,c);//链式前向星建边(为了djstl算法)
mp[u][v]=mp[v][u]=c;//邻接表建边(为了快速查询)
}
//以上为输入
//因为城管是从上一个城市走直达路径到下一个城市
//所以使用邻接表查边肯定会更快
int now=0;//now为现在城管在的路径开始时间
for(int i=1;i<g;i++){
l[de[i-1]][de[i]]=l[de[i]][de[i-1]]=now;
//上一个城市为de[i-1] ,当前城市为de[i]
//l为路径开始时间,所以直接等与now
now+=mp[de[i-1]][de[i]];
//走过这条路,所以now要加上我对应路的所需时间
//这样now就是下条路的开始时间
r[de[i-1]][de[i]]=r[de[i]][de[i-1]]=now-1;
//r为路径的结束时间,与下条路的开始时间关系是链接的,所以为now-1
}
djstl();
cout << dist[b]-k << endl;
//因为dist里存的是最快到对应点的对应时间,而题目要求的是你走的这段路消耗的时间
//所以需要减去开始时间k
return 0;
}