原题连接
POJ3635
这道题是给定一张城市之间有权无向图,每个城市都有加油站,但是每个加油站价钱不一样,对于多次询问,给定两城市和油缸容量,问最少花费。
思路:对于最短或最少问题,可以用BFS解决,但是这道题并不是求最短路径,需要结合优先队列进行变形。
可以用结构体+数组表示状态,当前所在城市、当前油量以及花费
对于每个城市,有两种分支选择
1)在这个城市加一升油,之所以选择加一升,是寻找最优解,并将当前状态加入优先队列。这里的一升并不是只在当前城市加一升油,因为已将当前状态置入队列,当其是最优,或者从队列中弹出时还可以继续进行加油操作。
2)通过链式向前星寻找当前城市可继续行驶的城市(油量充足、连通)。
因为是优先队列大大减少了不必要的循环。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#define N 1001
#define M 10100
using namespace std;
int n,m,s,t,c;
int head[N],next[2*M],ver[2*M],cost[2*M],tot=-1;
int fuel[N];
int ans[N][102];
bool vis[N][102];
void add(int x,int y,int val){
cost[++tot]=val;
ver[tot]=y;
next[tot]=head[x];
head[x]=tot;
}
struct Node
{
int u,f,w;//u当前城市、f剩余油量、w当前花费
Node(int a,int b,int c):u(a),f(b),w(c){}
friend bool operator < (Node a,Node b){
return a.w>b.w;
}
};
priority_queue <Node> q;
bool test1(int u,int f){
if(!vis[u][f+1]&&f+1<=c&&(ans[u][f+1]>ans[u][f]+fuel[u]))
return 1;
return 0;
}
bool test2(int v,int f,int p,int w){
if(f>=p && !vis[v][f-p] && ans[v][f-p]>w)
return 1;
return 0;
}
int bfs(){
memset(ans,0x3f,sizeof(ans));
memset(vis,false,sizeof(vis));
while(!q.empty()) q.pop();
ans[s][0]=0;
Node now1(s,0,0);
q.push(now1);
while(!q.empty()){
Node now=q.top();q.pop();
int u=now.u;
int f=now.f;
int w=now.w;
vis[u][f]=true;
if(u==t) return w;
if(test1(u,f)){
ans[u][f+1]=ans[u][f]+fuel[u];
q.push(Node(u,f+1,ans[u][f+1]));
}
for(int i=head[u];i;i=next[i]){
int y=ver[i];
int spend=cost[i];
if(test2(y,f,spend,w)){
ans[y][f-spend]=w;
q.push(Node(y,f-spend,w));
}
}
}
return -1;
}
int main()
{
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=0;i<n;i++){
cin>>fuel[i];
}
for(int i=1;i<=m;i++){
int x,y,z;
cin>>x>>y>>z;
add(x,y,z);
add(y,x,z);
}
int num;
cin>>num;
while(num--){
cin>>c>>s>>t;
int anss=bfs();
if(anss==-1)
printf("impossible\n");
else
printf("%d\n",anss);
}
return 0;
}