算法描述:最省賽程

最省賽程

描述

爲了讓自己能夠駕馭大師摩托,打開了大師摩托的隱藏任務:“賽車試煉”。

然而這個特殊的賽車試煉,竟然比的不是車速,比的是如何“省”油錢。

林克需要駕駛不同郵箱容量各異的賽車,從起點城市S開到終點城市E。

有N個城市(編號0、1…N-1)和M條賽道(構成一張無向圖)。

在每個城市裏邊都有一個加油站,不同的加油站的單位油價不一樣(有些城市油價貴,有些城市油價便宜些)。

請計算,如果林克駕駛的是一輛油箱容量爲C的賽車,那麼他從起點城市S開到終點城市E至少要花多少油錢?

注意:車子初始時油箱是空的,需要在起點城市加油方可起行。

輸入

第一行包含兩個整數N和M。

第二行包含N個整數,代表N個城市的單位油價,第i個數即爲第i個城市的油價Pi。

接下來M行,每行包括三個整數u,v,d,表示城市u與城市v之間存在道路,且賽車從u到v需要消耗的油量爲d。

接下來一行包含一個整數q,代表問題數量(q<100)

接下來q行,每行包含三個整數C、S、E,分別表示賽車油箱容量、起點城市S、終點城市E。

數據範圍:

image.png

輸出

對於每個問題,輸出一個整數,表示所需的最少油錢。

如果無法從起點城市開到終點城市,則輸出”impossible”。

每個結果佔一行。

輸入樣例

5 5
10 10 20 12 13
0 1 9
0 2 8
1 2 1
1 3 11
2 3 7
2
10 0 3
20 1 4

輸出樣例

170
impossible

題解

優先隊列+dijkstra+BFS

狀態空間(city,fuel,money):城市+賽車油量+累計費用

把城市+賽車的油量作爲一個狀態(city,fuel,money) 問題轉化爲從(start,0,0) 到 (end,0,0)的最短路徑

在當前城市買一升油(何時需要買油? )

在當前城市不買油,直接開往下一個城市

代碼

#include<bits/stdc++.h>
using namespace std;
//Head 與 next 數組存儲的是ver數組的下標,0表示指向空
//ver 數組存儲的是每條邊的終點
//使用數組實現鄰接表,無向圖
const int N = 1010;   //頂點數
const int M = 10100;  //邊數
const int maxCapcity = 108;  //油箱容量
int oilPrice[N],expenses[N][maxCapcity];
int head[N],Next[2*M],ver[2*M],dist[2*M],tot=-1;
bool visited[N][maxCapcity];
void add(int x,int y,int z)
{
    ver[++tot] = y;    //這條邊到達的點
    Next[tot] = head[x];  //鏈接
    head[x] = tot;    //標記x起點
    dist[tot] = z;  //權值
}
void printU(int u)
{
    //! 訪問從u出發的所有邊,當next[i]=0時遍歷結束
    cout<<u<<":";
    for(int i=head[u];i;i=Next[i]){
        int v = ver[i];
        int d = dist[i];
        cout<<"("<<u<<","<<v<<","<<d<<")";
    }
}
//狀態空間
struct node
{
    int city,fuel,money;
    node(int x,int y,int z):city(x),fuel(y),money(z){};
    friend bool operator<(node a,node b)
    {
        return a.money > b.money;
    }
};
priority_queue<node> q;
//是否需要買油
bool buyOneOil(int city,int fuel,int c)
{
    //如果油還在油箱限制內,且這次買油一定更好的話
    if(fuel+1<=c && !visited[city][fuel+1] &&
        (expenses[city][fuel+1]>expenses[city][fuel]+oilPrice[city]))
    return true;
    else return false;
}
//判斷下一條路是否合法
bool isNextRoad(int fuel,int cityid,int d,int money)
{
    //剩餘油大於路徑花費油,下一個狀態未被訪問過、並且花費更便宜
    if(fuel>=d && !visited[cityid][fuel-d] && 
        expenses[cityid][fuel-d]>money)
        return true;
    else
        return false;
}
//全局遍歷列表
int n,m;

//Dijkstra算法
int BFS(int currentCapcity,int start,int target)
{
    while (!q.empty())
    {
        q.pop();
    }
    memset(visited,false,sizeof(visited));
    memset(expenses,0x3f,sizeof(expenses));
    //從起點開始搜索
    expenses[start][0] = 0;
    q.push(node(start,0,0));   //起點,剩餘油量,權值
    while(!q.empty())
    {
        node qHead = q.top();
        q.pop();
        int city = qHead.city;
        int fuel = qHead.fuel;
        int money = qHead.money;
        visited[city][fuel] = true;
        //到達終點
        if(city==target) return money; 
        if(buyOneOil(city,fuel,currentCapcity))
        {
            //加上一升油
            expenses[city][fuel+1] = expenses[city][fuel]+oilPrice[city];
            //購買一升油的狀態
            q.push(node(city,fuel+1,expenses[city][fuel+1]));
        }
        for(int i=head[city];i;i=Next[i])   //遍歷相鄰城市
        {
            int nextCity = ver[i],d=dist[i];    //此路徑耗油量爲d
            if(isNextRoad(fuel,nextCity,d,money))  //判斷
            {
                expenses[nextCity][fuel-d] = money;  //耗油費
                q.push(node(nextCity,fuel-d,money));
            }
        }
    }
     return -1;
}
int main()
{
    cin>>n>>m;
    //代表N個城市的單位油價,第i個數即爲第i個城市的油價pi
    for(int i=0;i<n;i++)
        cin>>oilPrice[i];
    //每行包括三個整數u,v,d,表示城市u與城市v之間存在道路,且賽車從u到v需要消耗的油量爲d
    for(int i=1;i<=m;i++)
    {
        int u,v,d;
        cin>>u>>v>>d;
        add(u,v,d);
        add(v,u,d);  //無向圖
    }
    int questions;
    cin>>questions;
    while (questions--)
    {
        //油箱容量,起始城市,目標城市
        int c,s,e;
        cin>>c>>s>>e;
        int expenses = BFS(c,s,e);
        if(expenses==-1)
            printf("impossible\n");
        else
            printf("%d\n",expenses);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章