题目描述&数据范围:
有N个城市(编号0、1…N-1)和M条道路,构成一张无向图。
在每个城市里边都有一个加油站,不同的加油站的单位油价不一样。
现在你需要回答不超过100个问题,在每个问题中,请计算出一架油箱容量为C的车子,从起点城市S开到终点城市E至少要花多少油钱?
注意: 假定车子初始时油箱是空的。
输入格式
第一行包含两个整数N和M。
第二行包含N个整数,代表N个城市的单位油价,第i个数即为第i个城市的油价pi。
接下来M行,每行包括三个整数u,v,d,表示城市u与城市v之间存在道路,且车子从u到v需要消耗的油量为d。
接下来一行包含一个整数q,代表问题数量。
接下来q行,每行包含三个整数C、S、E,分别表示车子油箱容量、起点城市S、终点城市E。
输出格式
对于每个问题,输出一个整数,表示所需的最少油钱。
如果无法从起点城市开到终点城市,则输出”impossible”。
每个结果占一行。
数据范围
1≤N≤1000,
1≤M≤10000,
1≤pi≤100,
1≤d≤100,
1≤C≤100
题目中有N个城市(最多1k...dij怎么写呢?因为含有两种元素一种是边的距离第二种是油量.所以我们可以把{所付代价,到达点,油量}看成一个集合.起点是{0,S,0},终点就是{ans,E,0}.我们每次进行如下操作即可覆盖所有情况,完成dij.
首先把值定为inf,每次取出代价最少的答案.然后进行扩展,把最少的扩展了就不会再扩展了(因为边权为正),然后进行下一步操作. 扩展分为2步即可覆盖所有情况. 1.把当前点的油量+1然后放进堆,油量加1,代价更新,路径不变. 2.假如当前油量可以直接走完现在的路程,那么就直接走完一段路,油量减少路径更新.
上面即覆盖了最大的情况了,考虑建边很多,我们使用堆优化的dij.我们大概点数为NC(最多的情况啦)1e5.然后边数为100^1000.复杂度就是1e8log100(4)
大概那么多了--当然肯定不会有这么多,2s,dij肯定能过的.
下面就是代码实现了
#include <bits/stdc++.h> using namespace std; const int N=1e3+5; const int C=1e2+5; int p[N];//每个点的油价 bool st[N][C]; int dis[N][C]; struct node { int val,pos,oil;//当前位子代价,当前位子,剩余油量 bool operator<(const node &A)const{ return val>A.val; } }; struct vv { int to,len;//要到达的地方,以及边的长度 }; vector<vv>v[N]; int dij(int a,int b,int c)//a起点 b终点 c油箱容量 { memset(st,false,sizeof st); memset(dis,0x3f,sizeof dis); priority_queue<node>q; q.push({0,a,0});//先把起点推进去.三种元素{所付代价,到达点,油量} dis[a][0]=0; while(q.size()) { auto t=q.top();//取出堆顶元素 q.pop(); if(t.pos==b) return t.val;//假如堆顶是终点就直接返回答案. if(st[t.pos][t.oil]) continue;//假如记录过就直接跳过 st[t.pos][t.oil]=true; if(t.oil<c)//假如现在还可以+1升油 { if(dis[t.pos][t.oil+1]>t.val+p[t.pos]) { q.push({t.val+p[t.pos],t.pos,t.oil+1}); dis[t.pos][t.oil+1]=t.val+p[t.pos]; } } for(int i=0;i<v[t.pos].size();i++)//假如现在的油量还可以走到下一个城市 { int j=v[t.pos][i].to;//我要去的城市 if(t.oil>=v[t.pos][i].len)//假如我现在的油量已经大于这条路的长度了,那么我就可以去. { if(dis[j][t.oil-v[t.pos][i].len]>t.val) { q.push({t.val,j,t.oil-v[t.pos][i].len}); dis[j][t.oil-v[t.pos][i].len]=t.val; } } } } return -1; } int main() { int n,m; scanf("%d%d",&n,&m);//n个点 m条边 for(int i=0;i<n;i++) scanf("%d",&p[i]);//n个点的代价 while(m--) { int a,b,c; scanf("%d%d%d",&a,&b,&c);//a b建条长度为c的边 v[a].push_back(vv{b,c}); v[b].push_back(vv{a,c}); } int t; scanf("%d",&t); while(t--) { int a,b,c; scanf("%d%d%d",&c,&a,&b);//查询a为起点b为终点油的容量为c的最小花费 if(dij(a,b,c)==-1) puts("impossible"); else printf("%d\n",dij(a,b,c)); } return 0; }