题意:有N个城市M条双向道路,每个城市都有一个加油站和单位加油费用,每条边都有一个权值,经过这条边需要消耗油箱中边权数量的油。现在有Q组询问,每次给定油箱容量C、起点S和终点T,问从S到T的最小花费是多少,若无法到达输出“impossible”。
思路:
对于每次扩展都有各自不同代价时,求出从起始状态到每个状态的最小状态的问题,相当于在一张带权图上跑最短路。因此对于这类问题,有两种解决方案:
1、对搜索树进行重复遍历与更新,直至收敛到最优解,这用到了“迭代思想”,相当于SPFA,时间复杂度由O(n)到O(n^2)。
2、优先队列BFS,每次取出当前代价最小的状态进行扩展。根据广搜的单调性,对于一个状态,当第一次遍历到它时,即得到了到达这个状态的最优解。由于每个状态只扩展一次,所以优先队列BFS的时间复杂度是O(nlogn),相当于Dijkstra。
//------------以上内容来自 李煜东 神犇的著作 《算法竞赛进阶指南》-------------//
对于本题,相当于求从S到T的最短路。
这里有个小优化:对于每个状态,我们可以令其向自己转移一下,而不用把接下来所有的状态一次转移完。
例如本题,对于每个城市,若当前油箱中有rest单位的油,油箱容量为c,我们不必一次性将rest+1~c的状态加入队列,而可以将rest+1加入队列,这样终归会将所有状态遍历一遍,还省去了不少复杂操作。
还有就是vis数组判重时,最好在该状态从队列中取出时再标记,否则会漏掉部分状态。
P.S.对于impossible的情况,好像不需要并查集来维护连通性。。
AC Code:
#include#include #include #include using namespace std; const int N=1000+100,M=10000+100,C=100+100; //邻接表 struct node{ int to,nxt,w; }e[M<<1]; int head[N],tot; void add(int u,int v,int w){ e[++tot]=(node){v,head[u],w}; head[u]=tot; } int p[N];//price //并查集维护连通性 int fa[N]; int find(int x){ return fa[x]==x?x:fa[x]=find(fa[x]); } //BFS struct poi{ int vec,fuel,d; }; //queue q; priority_queueq; bool operator < (const poi& a,const poi& b){ return a.d>b.d; } bool vis[C][N]; int dis[C][N]; void BFS(int s,int t,int c){ while(q.size()) q.pop(); memset(vis,0,sizeof(vis)); memset(dis,0x3f,sizeof(dis)); q.push((poi){s,0,0}); vis[0][s]=1; dis[0][s]=0; while(q.size()){ poi tmp=q.top();q.pop(); int u=tmp.vec,rest=tmp.fuel,dist=tmp.d; if(u==t) { printf("%d\n",dist); return ; } vis[rest][u]=1; for(int i=head[u];i;i=e[i].nxt){ int v=e[i].to; if(rest 1][u]&&dis[rest+1][u]>dist+p[u]){ q.push((poi){u,rest+1,dist+p[u]}); dis[rest+1][u]=dist+p[u]; } if(rest>=e[i].w&&!vis[rest-e[i].w][v]&&dis[rest-e[i].w][v]>dist){ q.push((poi){v,rest-e[i].w,dist}); dis[rest-e[i].w][v]=dist; } } } printf("impossible\n"); } int main(){ int n,m; scanf("%d%d",&n,&m); for(int i=0;i ){ scanf("%d",&p[i]); } for(int i=1;i<=n;i++) fa[i]=i; for(int i=1;i<=m;i++){ int u,v,w; scanf("%d%d%d",&u,&v,&w); add(u,v,w);add(v,u,w); if(find(u)!=find(v)) fa[find(u)]=find(v); } int q; scanf("%d",&q); for(int i=1;i<=q;i++){ int c,x,y; scanf("%d%d%d",&c,&x,&y); if(find(x)!=find(y)){ printf("impossible\n"); continue; } BFS(x,y,c); } return 0; }