实验目的、内容及要求: 实验目的: 1.掌握分支限界法的基本思想 2.使用队列式分支限界法或优先队列式分支限界法求解实际问题 3. 在分支限界法中使用剪枝技巧 实验内容: 有n(n<=1000)个加油站,每个加油站每次能加1个单位容量的汽油,但是价格不同。两个加油站之间可以通过公路相连,通过每条公路所消耗的油量是已知的,公路数量为m(m≤100)。某人驾驶一辆汽车,最开始油箱是空的。给出q(q≤100)个询问,每个查询包含三个整数c、s和e,分别表示车的油箱的容积(c≤100)以及两个加油站的编号。要求针对每个查询,计算汽车从加油站s开到加油站e的最小加油费用。 输入包含多组测试数据。每一组测试数据的第一行包含两个整数n和m,分别表示加油站的数量和公路的数量,接下来一行有n个正整数表示每个加油站的单位油价,之后有m行,每行包含三个数字u、v、d,描述一条公路所连接的两个加油站的编号以及消耗的油量。再接下来一行有一个正整数q表示询问数,之后的q行每行描述一个询问,一个询问包含三个整数c、s、e,分别表示油箱容量和起止的加油站编号。 对于每个询问,输出一个非负整数表示全程的最小加油费用,若不存在则输出'impossible'。 题目来源:POJ3635(http://poj.org/problem?id=3635) 要求:撰写实验代码,提交到POJ的结果必须是Accept。 |
实验仪器设备(实验环境): 普通主流PC,英特尔I5处理器2.0GHz,8GDDR4内存,500G硬盘,19寸液晶显示器。 |
实验过程(包括实现思路、实验步骤/实现代码、实验结果/运行图): 将加油站看作图中的顶点,公路看作图中的双向边,加油站的加油费用可以看作顶点的权值,公路消耗的油量可以看作相应边上的权值,构建一个图形结构。 问题所求不是从初始状态到目标状态要经过多少步,而是要求从出发加油站到目标加油站的最小的加油费用总和,于是使用优先队列式分支限界法,以最小消耗优先进行搜索。 搜索过程中,在不同状态下,汽车所在的加油站、油箱里的汽油的体积、已经产生的加油费用总和是不同的,因此,优先队列中的状态结点应该包含当前所在的加油站编号、剩余油量以及已经产生的费用等信息。显然,在优先队列中,结点的优先级确定为:一个结点已经产生的费用越小,这个结点的优先级越高。 对于某个状态结点,构造后续状态结点的时候,要分两种情况。 一种是在当前加油站加了油,后继状态结点对应的加油站依旧是当前加油站,此时剩余油量需要加上所加的油,已经产生的费用要加上在加油站u加油的费用。 一种情况是在当前加油站没有加油就驶向了下一个加油站,这时候需要遍历当前加油站对应顶点的出边可以到达的其他加油站,可能产生多个后继状态结点,这些后继状态结点对应的加油站是某个与当前加油站相邻的加油站,剩余油量需要减去相应公路上消耗的油,已经产生的费用不变。 具体代码如下:#include <iostream> #include <algorithm> #include <queue> #include <vector> using namespace std; const int INF = 0x3f3f3f3f; int n,m; // 城市 道路 bool flag = false; struct City{ int v; // 城市 int d; // 长度 City(int v,int d):v(v),d(d){} }; struct Cost{ int city; // 到达的城市 int fuel; // 剩余油量 int costs; // 花费 Cost(int c,int f,int costs):city(c),fuel(f),costs(costs){} bool operator < (const Cost b) const{ return costs > b.costs; } }; int price[1005]; // 油价 int dp[1005][101]; // 状态,到达城市i,剩余油量j的最小花费 vector<City> vec[10005]; // 邻接表 bool visited[1005][105]; // 标记被访问的状态 void Query(int c, int s, int e){ priority_queue<Cost> q; //建立小根堆,优先输出花费最小 q.push(Cost(s,0,0)); dp[s][0] = 0; while(!q.empty()){ Cost x = q.top(); q.pop(); int a = x.city; // 当前的城市 int b = x.fuel; // 当前油箱中的油量 if(visited[a][b]) continue; // 该状态访问过 if(x.city == e){ cout << x.costs <<endl; flag = true; break; } if(b < c){ // 加油,准备下一次旅行 if(dp[a][b] + price[a] < dp[a][b + 1]){ dp[a][b + 1] = dp[a][b] + price[a]; q.push(Cost(a,b + 1,dp[a][b + 1])); } } for(int i = 0; i < (int)vec[a].size(); i++){ int d = vec[a][i].d; int v = vec[a][i].v; if(b - d >= 0 && dp[a][b] < dp[v][b - d]){ // 这里也防止了油量溢出 dp[v][b - d] = dp[a][b]; q.push(Cost(v,b - d, dp[v][b - d])); } } visited[a][b] = true; } } int main(){ cin >> n >> m; for(int i = 0;i < n; i++){ cin >> price[i]; } for(int i = 0;i < m; i++){ int u,v,d; cin >> u >> v >> d; vec[u].push_back(City(v,d)); vec[v].push_back(City(u,d)); } int t; // 查询次数 cin >> t; while(t--){ int C,s,e; // 油箱容量,起点,终点 cin >> C >> s >> e; memset(visited,false,sizeof(visited)); for(int i = 0;i <= n;i++){ for(int j = 0;j <= C;j++){ dp[i][j] = INF; } } flag = false; Query(C,s,e); if(flag == false) cout << "impossible" << endl; } return 0; } |
分支限界法实验
猜你喜欢
转载自blog.csdn.net/weixin_63747428/article/details/134615692
今日推荐
周排行