CSUOJ 1891 Full Tank? 最短路 bfs

Description
After going through the receipts from your car trip through Europe this summer, you realised that the gas prices varied between the cities you visited. Maybe you could have saved some money if you were a bit more clever about where you filled your fuel?
To help other tourists (and save money yourself next time), you want to write a program for finding the cheapest way to travel between cities, filling your tank on the way. We assume that all cars use one unit of fuel per unit of distance, and start with an empty gas tank.

Input
The first line of input gives 1<=n<=1000 and 0<=m<=10000, the number of cities and roads. Then follows a line with n integers 1<=pi<=100, where pi is the fuel price in the ith city. Then follow m lines with three integers 0<=u, v < n and 1<=d<=100, telling that there is a road between u and v with length d. Then comes a line with the number 1<=q<=100, giving the number of queries, and q lines with three integers 1<=c<=100, s and e, where c is the fuel capacity of the vehicle, s is the starting city, and e is the goal.

Output
For each query, output the price of the cheapest trip from s to e using a car with the given capacity, or “impossible” if there is no way of getting from s to e with the given car.

Sample Input
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
Sample Output
170
impossible
Hint

题意:
有m座城市 每座城市的单位油价各不相同,城市之间由若干条道路相连,一单位路程消耗一单位的油料,对于每次询问,求出最小花费。
第一行:给你n座城市,m条边
第二行:给你n座城市的每单位油的价格
接下来m行:每行依次给出此条边的起点 终点 距离
第m+3行:q次询问
接下来q行:依次给出油箱容量 起点 目的地

  • 1.给出点,边,求最值花费。很容易看出这是一个最短路有关问题。最多1000个点 无法用Floyd求解

  • 2.无负边 每个点可以分出茫茫多种状态

  • 3.所以要用Dijkstra+优先队列优化求解

每次询问最多拓展的状态数是城市数量*(油箱容量+1)
经过剪枝后最多搜索1000*101个状态。枚举所有状态的复杂度是在限制范围内的。
状态拓展:
if(countss < c&&costs + cost[citys] < dp_cost[citys][countss+1]) {
dp_cost[citys][countss+1]=costs+cost[citys]; PQ.push(statu(citys,costs+cost[citys],countss+1));
}
每次都加一单位油 如果加完油的状态没被搜过 则加入队列。
剪枝: 设当前需压入队列的的状态为;statu next;

  • int vis[max_city][max_full]; // statunextvis[i][j]表示 到达城市i时 油箱容量为j的情况

  • int dp_cost[max_city][max_full];// dp_cost[i][j]表示 到达城市i时 油箱容量为j的情况 下最小花费。

    只有vis[next.city][next.counts]==0&&dp_cost[next.city][next.counts]>next.sum_cost
    才将next压入队列 并不断维护dp_cost数组。
    假设目的地为end 则如果可以到达end 则答案会出存在dp_cost[end][0]内。

#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<algorithm>
#include<functional>
#include<algorithm>
#include<string.h>
using namespace std;
const int max_full=105;
const int max_city=1005;
class statu// 储存当前状态
{
public:
    int city,sum_cost,counts;// 当前所在城市 总花费 油箱剩余油量
    statu(int city,int sum_cost,int counts):city(city),sum_cost(sum_cost),counts(counts) {}
    statu() {}
    bool operator < (const statu a) const
    {
        return this->sum_cost>a.sum_cost;
    }
};
class edge// 存边
{
public:
    int to,dis;//通向哪  距离
    edge(int to,int dis):to(to),dis(dis) {}
};
vector<edge> V_dis[1005];
int vis[max_city][max_full];
int cost[max_city];
int dp_cost[max_city][max_full];
bool bfs(int c,int s,int e)
{
    priority_queue<statu> PQ;
    PQ.push(statu(s,0,0));//压入初始状态
    memset(vis,0,sizeof vis);
    memset(dp_cost,127,sizeof dp_cost);
    while(!PQ.empty())
    {
        statu now=PQ.top();
        PQ.pop();
        int citys=now.city,countss=now.counts,costs=now.sum_cost;
        if(citys==e)//到达目的地
        {
            if(dp_cost[now.city][now.counts]>now.sum_cost)// 维护dp_cost 数组
                dp_cost[now.city][now.counts]=now.sum_cost;
            return true;
        }
        if(countss < c&&costs+cost[citys] < dp_cost[citys][countss+1])//加油
        {
            dp_cost[citys][countss+1]=costs+cost[citys];
            PQ.push(statu(citys,costs+cost[citys],countss+1));
        }
        int sizes=V_dis[citys].size();
        for(int i=0; i<sizes; i++)//启程 走向下一个城市
        {
            edge e=V_dis[citys][i];
            statu next(e.to,now.sum_cost,countss-e.dis);
            if(next.counts<0)//判断油够不够
                continue;
            if(vis[next.city][next.counts])//剪枝
                continue;
            vis[next.city][next.counts]=1;
            if(dp_cost[next.city][next.counts]>next.sum_cost)
                dp_cost[next.city][next.counts]=next.sum_cost;
            PQ.push(next);
        }
    }
    return false;// 若无法到达目的地 则返回false
}
int main()
{
    int n,m;
    while(~scanf("%d %d",&n,&m))
    {
        for(int i=0; i<n; i++)
        {
            scanf("%d",&cost[i]);
        }
        for(int i=0; i<m; i++)//存边
        {
            int c,s,e;
            scanf("%d %d %d",&e,&s,&c);
            V_dis[s].push_back(edge(e,c));
            V_dis[e].push_back(edge(s,c));
        }
        int cases;
        scanf("%d",&cases);
        while(cases--)
        {
            int c,s,e;
            scanf("%d %d %d",&c,&s,&e);
            if(bfs(c,s,e))
                printf("%d\n",dp_cost[e][0]);//最优解到达目的地肯定不会有油剩余
            else
                puts("impossible");
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值