分支限界法实验

实验目的、内容及要求:

实验目的:

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