题意:
从起点到目的地,一路上有油价不同的加油站,选择最佳的加油方案,使加油钱最少。
思路:
就是两种可选择的方案
如果说当前还没有装满油的话,那么我们可以选择加一升汽油,然后拓展到新的状态,也就是(city,fuel+1)
对于每一条边而言,我们可以通过这条边到达一个城市,那么如果说当前剩余油量可以走完这条路,那么我们就拓展新状态(Next,fuel-w)
这样我们不断地取出当前花费最少的状态进行拓展,然后每一次拓展用ans数组记录最小花费即可.
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1010;
const int M=10100;
int wn[N],head[N],Next[2*M],ver[2*M],dis[2*M],ans[N][102];
bool vis[N][102];
int c,s,e,n,m,t,tot=-1;
void add(int x,int y,int z) {
ver[++tot]=y;
Next[tot]=head[x];
dis[tot]=z;
head[x]=tot;
}
struct node {
int u,f,w;
bool operator < (node a,node b) {
return a.w>b.w;//权值从小到大排序,是小根堆
}
};
priority_queue <node> q;
bool check1(int u,int f) {
if(f+1<=c && !vis[u][f+1] && (ans[u][f+1]>ans[u][f]+wn[u])) return true;//如果油还在没满,且在这买油一定更好
return false;
}
bool check2(int f,int v,int p,int w) {
if(f>=p && !vis[v][f-p] && ans[v][f-p]>w) return true;//没有访问过,且当前剩余油大于到下一加油站花费油
return false;
}
int BFS() {
while(!q.empty()) q.pop();
memset(vis,false,sizeof(vis));
memset(ans,0x3f,sizeof(ans));
ans[s][0]=0;
q.push(node(s,0,0));//s为起点,0为剩余油量,0为权值
while(q.size()) {
auto now=q.top();
q.pop();
int u=now.u;
int f=now.f;
int w=now.w;
vis[u][f]=true;
if(u==e) return w;
if(check1(u,f))//判断
{
ans[u][f+1]=ans[u][f]+wn[u];//加一升油的钱
q.push(node{u,f+1,ans[u][f+1]});//购买一升油的状态
}
for(int i=head[u];i;i=Next[i])//遍历
{
int v=ver[i],p=dis[i];
if(check2(f,v,p,w))//判断
{
ans[v][f-p]=w;//选择不加油到下一城市
q.push(node{v,f-p,w});
}
}
}
return -1;
}
int main()
{
cin>>n>>m;
for(int i=0; i<n; i++) cin>>wn[i];
for(int i=1; i<=m; i++) {
int x,y,z;
cin>>x>>y>>z;
add(x,y,z);
add(y,x,z);
}
cin>>t;
while(t--) {
cin>>c>>s>>e;
int ans=BFS();
if(ans==-1) printf("impossible\n");//无解
else printf("%d\n",ans);
}
return 0;
}