light oj 1254 - Prison Break (Dijkstra 汽车加油行驶最优问题 最短路)

23 篇文章 0 订阅

1254 - Prison Break
Time Limit: 2 second(s)Memory Limit: 32 MB

Michael Scofield has just broken out of the prison. Now he wants to go to a certain city for his next unfinished job. As you are the only programmer on his gang, he asked your help. As you know that the fuel prices vary in the cities, you have to write a code to help Scofield that instructs him where to take the fuel and which path to choose. Assume that his car uses one unit of fuel in one unit of distance. Now he gives you the starting city s where he starts his journey with his car, the destination city t and the capacity of the fuel tank of his car c, the code should find the route that uses the cheapest fuel cost. You can assume that Scofield's car starts with an empty fuel tank.

Input

Input starts with an integer T (≤ 5), denoting the number of test cases.

Each case starts with a line containing two integers n (2 ≤ n ≤ 100) and m (0 ≤ m ≤ 1000) where n denotes the number of cities and m denotes the number of roads. The next line contains n space separated integers, each lies between 1 and 100. The ith integer in this line denotes the fuel price (per unit) in the ithcity. Each of the next m lines contains three integers u v w (0 ≤ u, v < n, 1 ≤ w ≤ 100, u ≠ v) denoting that there is a road between city u and v whose length is w.

The next line contains an integer q (1 ≤ q ≤ 100) denoting the number of queries by Scofield. Each of the next q lines contains the request. Each request contains three integers: c s t (1 ≤ c ≤ 100, 0 ≤ s, t < n) where c denotes the capacity of the tank, s denotes the starting city and t denotes the destination city.

Output

For each case, print the case number first. Then for each query print the cheapest trip from s to t using the car with the given capacity c or 'impossible' if there is no way of getting from s to t with the given car.

Sample Input

Output for Sample Input

1

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

Case 1:

170

impossible

 



题意:n个点m条边的有向图,q次询问c,s,t,表示汽车邮箱容量为c,求从起点s到终点t的最小费用。汽车在每个点可以加任意的油,每个点的油价为a[i]。

思路:优先队列的Dij,每个节点保存还剩下的油量fuel和到当前为止所用的花费,dis[i][j]表示在i点油量还剩下j的费用最小值。注意,每次入队列不要一次性把所有符合的油量全部入队列,比如u->v,距离为w,那么油量区间w~c之间的油量都是可以满足从u走到v的,但不要一次性把w~c全部入队列,这样会超时:应该这样:对于队列中的每个点u先看它当前的油量能否顺着边走到下一个节点v去更新v,如果可以就更新v,然后当前点u的油量还可以增加就把它的油量增加一再入队列。

代码:

#include <iostream>
#include <functional>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string>
#include <map>
#include <stack>
#include <vector>
#include <set>
#include <queue>
#pragma comment (linker,"/STACK:102400000,102400000")
#define pi acos(-1.0)
#define eps 1e-6
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
#define FRE(i,a,b)  for(i = a; i <= b; i++)
#define FREE(i,a,b) for(i = a; i >= b; i--)
#define FRL(i,a,b)  for(i = a; i < b; i++)
#define FRLL(i,a,b) for(i = a; i > b; i--)
#define mem(t, v)   memset ((t) , v, sizeof(t))
#define sf(n)       scanf("%d", &n)
#define sff(a,b)    scanf("%d %d", &a, &b)
#define sfff(a,b,c) scanf("%d %d %d", &a, &b, &c)
#define pf          printf
#define DBG         pf("Hi\n")
typedef long long ll;
using namespace std;

#define INF 0x3f3f3f3f
#define mod 1000000009
const int maxn = 1005;
const int MAXN = 105;
const int MAXM = 200010;
const int N = 1005;

struct Node
{
    int id,fuel,cost;
    Node(){}
    Node(int a,int b,int c):id(a),fuel(b),cost(c){}
    bool operator<(Node a) const
    {
        return cost>a.cost;
    }
}node[MAXN];

struct Edge
{
    int u,v,w,next;
}edge[MAXM];

int n,m,c,s,t;
int num,head[MAXN],dis[MAXN][MAXN];
int a[MAXN];

void init()
{
    num=0;
    memset(head,-1,sizeof(head));
}

void addedge(int u,int v,int w)
{
    edge[num].u=u;
    edge[num].v=v;
    edge[num].w=w;
    edge[num].next=head[u];
    head[u]=num++;
}

void Dijkstra(int c,int s,int t)
{
    int u,v;
    Node st,now;
    priority_queue<Node>Q;
    memset(dis,INF,sizeof(dis));
    dis[s][0]=0;
    Q.push(Node(s,0,0));
    while (!Q.empty())
    {
        st=Q.top();
        Q.pop();
        u=st.id;
        if (dis[u][st.fuel]<st.cost) continue;
        for (int i=head[u];~i;i=edge[i].next)
        {
            v=edge[i].v;
            if (st.fuel>=edge[i].w&&dis[v][st.fuel-edge[i].w]>dis[u][st.fuel])  //更新下一个节点
            {
                dis[v][st.fuel-edge[i].w]=dis[u][st.fuel];
                Q.push(Node(v,st.fuel-edge[i].w,dis[u][st.fuel]));
            }
        }
        if (st.fuel<c&&dis[u][st.fuel+1]>dis[u][st.fuel]+a[u])  //当前点的油量还可以增加1
        {
            dis[u][st.fuel+1]=dis[u][st.fuel]+a[u];
            Q.push(Node(u,st.fuel+1,dis[u][st.fuel]+a[u]));
        }
    }
    int ans=INF;
    for (int i=0;i<=c;i++)
        ans=min(dis[t][i],ans);
    if (ans==INF) printf("impossible\n");
    else printf("%d\n",ans);
}

int main()
{
#ifndef ONLINE_JUDGE
    freopen("C:/Users/lyf/Desktop/IN.txt","r",stdin);
#endif
    int i,j,e,u,v,w,q,cas=0;
    scanf("%d",&e);
    while (e--)
    {
        scanf("%d%d",&n,&m);
        for (i=0;i<n;i++) scanf("%d",&a[i]);
        init();
        for (i=0;i<m;i++)
        {
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,v,w);
            addedge(v,u,w);
        }
        scanf("%d",&q);
        printf("Case %d:\n",++cas);
        while (q--)
        {
            scanf("%d%d%d",&c,&s,&t);
            Dijkstra(c,s,t);
        }
    }
    return 0;
}




  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1.问题描述 给定一个N*N 的方形网格,设其左上角为起点,坐标为(1,1),X 轴向右为正,Y 轴 向下为正,每个方格边长为1。一辆汽车从起点出发驶向右下角终点,其坐标为(N,N)。 在若干个网格交叉点处,设置了油库,可供汽车行驶途中加油汽车行驶过程中应遵守 如下规则: (1)汽车只能沿网格边行驶,装满油后能行驶K 条网格边。出发时汽车已装满油,在 起点与终点处不设油库。 (2)当汽车行驶经过一条网格边时,若其X 坐标或Y 坐标减小,则应付费用B,否则 免付费用。 (3)汽车行驶过程中遇油库则应加满油并付加油费用A。 (4)在需要时可在网格点处增设油库,并付增设油库费用C(不含加油费用A)。 (5)(1)~(4)中的各数N、K、A、B、C均为正整数。 算法设计: 求汽车从起点出发到达终点的一条所付费用最少的行驶路线。 数据输入: 输入数据。第一行是N,K,A,B,C的值,2 <= N <= 100, 2 <= K <= 10。第二行起是一个N*N 的0-1方阵,每行N 个值,至N+1行结束。方阵的第i 行第j 列处的值为1 表示在网格交叉点(i,j)处设置了一个油库,为0 时表示未设油库。 各行相邻的2 个数以空格分隔。 结果输出: 将找到的最优行驶路线所需的费用,即最小费用输出. Sample input 9 3 2 3 6 0 0 0 0 1 0 0 0 0 0 0 0 1 0 1 1 0 0 1 0 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 1 1 0 0 1 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1 1 0 0 1 0 0 0 1 0 0 1 0 0 0 0 0 0 0 Sample output 12

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值