原题链接 [TJOI2019]大中锋的游乐场
题目描述
大中锋正在一个游乐场里玩耍。游乐场里有很多娱乐设施,娱乐设施之间相互有道路相连,经过每一条路都需要花费一定的时间。
为了方便游客,每一个娱乐设施旁都会配有一个小卖部,一部分小卖部会销售可乐,另一部分会销售汉堡。
由于大中锋十分贪吃,所以每当他走到一个娱乐设施,他都会先去购买一杯可乐或一个汉堡,并把它们吃掉。
但如果大中锋吃掉的汉堡数量比他喝掉的可乐数量多于k,那他就会感到很渴;如果喝掉的可乐数量比吃掉的汉堡数量多于k,那他就会感到很饿.
现在大中锋正在第 a 个娱乐设施,他想前往第 b 个娱乐设施,但在他前进的路途中他不希望自己很渴或很饿。
大中锋想知道自己在路上少花费多少时间。但由于大中锋很懒惰,他不想思考这个问题。你能帮助他解决这个问题吗?
注意:大中锋非常贪吃,所以他到达每个点的第一件事是去吃(或者喝),才考虑其他的事情,所以在起始点和终点他都会去买汉堡(可乐),
你也需要保证在这两个点他不会感到很饿或者很渴。
输入输出格式
输入格式:
多样例输入,第一行输入一个正整数 TT 表示样例数。
对于每一个样例:
第一行三个数字 n,m,k , n 代表游乐场一共有多少个娱乐设施, m 代表游乐场一共有多少条道路, k 的意义如题面中所述。
接下来有一行 n 个数字,第 i 个数字代表第 i 个小卖部销售的是什么, 1 代表可乐, 2 代表汉堡。
接下来有 m 行输入,每行三个数字 p,q,t ,代表从第 p 个娱乐设施到第 q 个娱乐设施有一条双向道路,通过这条道路需要花费 t 单位时间。
最后一行有两个整数 a,b,代表大中锋想从娱乐设施 a 前往娱乐设施 b 。
输出格式:
每组样例输出一行整数 t ,代表大中锋在路上既不会感到很渴也不会感到很饿的情况下,
从娱乐设施 a 到娱乐设施 b花费的最少时间,如果无法达到,输出 −1 。
输入输出样例
说明
数据范围
对于 30% 的数据, 1000n≤50,m≤1000
对于 100% 的数据, 10000n≤10000,m≤100000,k≤10,t≤10000
对于所有数据,保证 T ≤ 10T≤10 ,且每个样例点的大数据不超过 22 个。
题目补充说明
- 路径不一定是简单路径
- 大中锋可以多次经过一个节点,同时每次都会取得汉堡/可乐
题解
算法:Dijkstra单源最短路径
题目要求路程上花费的最短时间,故很容易想到最短路算法。
本题的特色在于求最短路时有一个限定条件——吃汉堡和可乐数量的差的绝对值不能超过k,
而k的范围非常小(k≤10),所以可以按照每个状态吃汉堡和可乐数量的差将每1个点拆成2k个点,
设dis(i,j)表示从起点走到i号节点且可乐数量-汉堡数量为j花费的最短时间;cola(i)表示i号点的可乐数量(卖可乐则为1,卖汉堡则为-1)
则dis(i,j)=min{dis(k,j-cola(i))+路径长度},用dijkstra即可
实现
代码很简单,核心是dijkstra部分:
1.初始化:
(1)定义node表示状态,用优先队列priority_queue存储
struct node{ int idx,tim,cola; node(int x,int y,int z){ idx=x; tim=y; cola=z; } friend inline bool operator<(node x,node y){ if(x.tim!=y.tim) return x.tim>y.tim; return x.cola>y.cola; } };
(2)宽搜前插入起点,可乐数量为k(为了防止可乐数量为负数,加上基数k,控制可乐数量在0~2k之间)
que.push(node(S,0,K+cola[S]));
2.转移:
根据dis(i,j)=min{dis(k,j-cola(i))+路径长度},套用dijkstra转移方程
for(int i=head[idx];i;i=nxt[i]){ int ii=col+cola[to[i]]; if((ii>=0&&ii<=K*2)&&(!vis[to[i]][ii]&&tim+cst[i]<dis[to[i]][ii])){ dis[to[i]][ii]=tim+cst[i]; que.push(node(to[i],dis[to[i]][ii],ii)); } }
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 const int INF=0x3f3f3f3f,MAXN=1e4+50,MAXM=2e5+50,MAXK=21; 5 int sz,head[MAXN],nxt[MAXM],to[MAXM],cst[MAXM]; 6 inline void add(int x,int y,int z){ 7 nxt[++sz]=head[x]; head[x]=sz; to[sz]=y; cst[sz]=z; 8 nxt[++sz]=head[y]; head[y]=sz; to[sz]=x; cst[sz]=z; 9 } 10 int N,M,K,S,T,cola[MAXN]; 11 inline void Init(){ 12 scanf("%d%d%d",&N,&M,&K); 13 for(int i=1;i<=N;i++){ 14 scanf("%d",cola+i); 15 if(cola[i]==2) 16 cola[i]=-1; 17 } 18 for(int i=1;i<=M;i++){ 19 int ii,jj,kk; 20 scanf("%d%d%d",&ii,&jj,&kk); 21 add(ii,jj,kk); 22 } 23 scanf("%d%d",&S,&T); 24 } 25 struct node{ 26 int idx,tim,cola; 27 node(int x,int y,int z){ 28 idx=x; tim=y; cola=z; 29 } 30 friend inline bool operator<(node x,node y){ 31 if(x.tim!=y.tim) 32 return x.tim>y.tim; 33 return x.cola>y.cola; 34 } 35 }; 36 int dis[MAXN][MAXK],vis[MAXN][MAXK]; 37 inline void Dijkstra(){ 38 memset(dis,0x3f,sizeof(dis)); 39 memset(vis,0,sizeof(vis)); 40 priority_queue<node> que; 41 que.push(node(S,0,K+cola[S])); 42 while(que.size()){ 43 node cur=que.top(); 44 que.pop(); 45 int idx=cur.idx,tim=cur.tim,col=cur.cola; 46 if(vis[idx][col]||tim>dis[idx][col]) 47 continue; 48 vis[idx][col]=1; 49 for(int i=head[idx];i;i=nxt[i]){ 50 int ii=col+cola[to[i]]; 51 if((ii>=0&&ii<=K*2)&&(!vis[to[i]][ii]&&tim+cst[i]<dis[to[i]][ii])){ 52 dis[to[i]][ii]=tim+cst[i]; 53 que.push(node(to[i],dis[to[i]][ii],ii)); 54 } 55 } 56 } 57 } 58 int ans=INF; 59 inline void Output(){ 60 for(int i=0;i<=2*K;i++) 61 if(dis[T][i]<ans) 62 ans=dis[T][i]; 63 printf("%d ",ans==INF?-1:ans); 64 } 65 int Task; 66 int main(){ 67 scanf("%d",&Task); 68 while(Task--){ 69 Init(); 70 Dijkstra(); 71 Output(); 72 } 73 return 0; 74 }