题目大意:给定一张图,和每个点的油价,知道每条路的耗油量,给定一些询问,求从起点到终点用指定油箱容量的车所得到的最小耗费。
解题思路:BFS+优先队列
优先队列介绍:采用stl中的priority_queue实现。priority_queue默认的是最大优先队列,声明时只要priority_queue<int> q就行了。如果是最小堆,麻烦一些
priority_queue<int,vector<int>,cmp> q。其中cmp函数如下: struct cmp { bool operator()(const int &i,const int &j) { return i>j;//注意:是“>”而不是“<”。 } }
图存储:本题采用邻接表的形式存储图。采用stl中的vector实现。首先定义数据结构如下:
typedef struct { int e;//邻接点 int dist;//距离 }edge;
然后定义vector数组:
vector<edge> graph[N];//N为顶点数
vector实现邻接表比普通二维数组实现更优的地方在于它不用记录各个点邻接点的个数,而只用一个简单的push_back()函数就轻松实现了边的记录。
题目解答:1.到i城市所花的费用作为优先队列的权值。
2.定义如下结构体记录当前状态:
typedef struct { int p;//当前城市 int q;//油箱中剩余的油 int sp;//当前花费 }state;
3.定义标记数组isvis[N][M]记录到城市p还剩q升油这个状态。
4.初始化将开始状态(u,0,0)加入队列。
5.然后每次取优先队列队头元素对队头中的城市进行扩展(出队列的时候对isvis[p][q]进行标记)。可以选择加油或者不加油。加油的话每次加一升(如果加一升不超过油箱容量),然后相应的状态也进行改变(u,+1,+p)[其中p为当前城市的油价],如果当前状态能到下一个城市,则下一个城市的状态也入队列。然后循环执行5。直到出队列的城市是目的城市为止。
注意:1.因为队列是按花费为权值的,所以当一种状态已经被标记为真的时候,说明出来的花费已经是最小的了。所以队列后面出来的相同状态可以直接跳过,不做处理。
2.对当前城市进行扩展的时候,可能有扩展后的状态已经出队列了。这说明这个扩展后的状态比当前状态已经有了一个更优的解(优先队列的结果),而当前状态扩展的状态花费肯定比当前的多(因为扩展的时候加油,加油花钱)。所以,这个状态就不需要再进队列了。这是两个利用了优先队列性质进行的剪枝。
核心代码:
void bfs(){ int i,j,k,w; priority_queue<state,vector<state>,cmp> que; for(i=0;i<n;i++) { for(j=0;j<101;j++) { us[i][j]=MAX;//初始为无穷大 isvis[i][j]=false; } } state s; s.p=u; s.q=0; s.sp=0; us[u][0]=0; que.push(s); // isvis[s.p][s.q]=true; state cur,tmp; while(!que.empty()){ cur=que.top(); que.pop(); if(isvis[cur.p][cur.q]==true) continue; isvis[cur.p][cur.q]=true; if(cur.p==v) { res=cur.sp; return; } if(cur.q+1<=c&&isvis[cur.p][cur.q+1]==false)//当前加一升油之后不超过油箱容量 { us[cur.p][cur.q+1]=us[cur.p][cur.q]+p[cur.p]; tmp.p=cur.p; tmp.q=cur.q+1; tmp.sp=us[cur.p][cur.q+1]; que.push(tmp); } for(i=0;i<graph[cur.p].size();i++) { w=graph[cur.p][i].e; k=cur.q-graph[cur.p][i].dis;//从p城市到i城市还剩下的油 if(k>=0&&isvis[w][k]==false&&cur.sp<us[w][k])//当前的消费小于到城市w用油k升的最小费用 { us[w][k]=cur.sp; tmp.p=w; tmp.q=k; tmp.sp=cur.sp; que.push(tmp); // isvis[tmp.p][tmp.q]=true; } } } }
原文地址:http://blog.csdn.net/mmoaay/article/details/40426165