☆【最短路】速度限制

【问题描述】
    在这个繁忙的社会中,我们往往不再去选择最短的道路,而是选择最快的路线。开车时每条道路的限速成为最关键的问题。不幸的是,有一些限速的标志丢失了,因此你无法得知应该开多快。一种可以辩解的解决方案是,按照原来的速度行驶。你的任务是计算两地间的最快路线。
    你将获得一份现代化城市的道路交通信息。为了使问题简化,地图只包括路口和道路。每条道路是有向的,只连接了两条道路,并且最多只有一块限速标志,位于路的起点。两地A和B,最多只有一条道路从A连接到B。你可以假设加速能够在瞬间完成并且不会有交通堵塞等情况影响你。当然,你的车速不能超过当前的速度限制。
【输入】
    输入文件speed.in的第一行是3个整数N,M和D(2<=N<=150),表示道路的数目,用0..N-1标记。M是道路的总数,D表示你的目的地。接下来的M行,每行描述一条道路,每行有4个整数A(0≤A<N),B(0≤B<N),V(0≤V≤500)and L(1≤L≤500),这条路是从A到B的,速度限制是V,长度为L。如果V是0,表示这条路的限速未知。如果V不为0,则经过该路的时间T=L/V。否则T=L/Vold,Vold是你到达该路口前的速度。开始时你位于0点,并且速度为70。
【输出】
	输出文件speed.out仅一行整数,表示从0到D经过的城市。
	输出的顺序必须按照你经过这些城市的顺序,以0开始,以D结束。仅有一条最快路线。
【样例】
speed.in
6 15 1
0 1 25 68
0 2 30 50
0 5 0 101
1 2 70 77
1 3 35 42
2 0 0 22
2 1 40 86
2 3 0 23
2 4 45 40
3 1 64 14
3 5 0 23
4 1 95 8
5 1 0 84
5 2 90 64
5 3 36 40

speed.out
0 5 2 3 1
拆點+SPFA。

普通的SPFA中,用dist數組保存臨時最短距離,並用標誌數組保存是否在隊列中。

而此題,當前點臨時最短距離(即題目中的最短時間),還與從上一個來的初速度有關,所以將dist多加一個參數即前一個點的編號,並且在隊列中要記錄走過當前這條邊的速度。
Accode:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <bitset>
#include <cmath>
using std::bitset;

const char fi[] = "speed.in";
const char fo[] = "speed.out";
const int maxN = 210;
const int SIZE = 1050000;
const int MOD = (1 << 20) - 1;
const int MAX_int = 0x3fffff00;
const int MIN_int = -MAX_int;
const double MAX_double = 1e198;
const double MIN_double = -MAX_double;
const double zero = 1e-12;

struct Edge
  {int dest, _d, _v; Edge *next; };

struct Que
{
  int Last, ths, _v;
  Que(int la, int th, int v)
    {Last = la; ths = th; _v = v; }
  Que() {Last = ths = _v = 0; }
};

Edge *edge[maxN];
double dist[maxN][maxN];
Que q[SIZE];
bitset <maxN> marked[maxN];
int pre[maxN][maxN];
int n, m, start, finish, f, r;

  void init_file()
  {
    freopen(fi, "r", stdin);
    freopen(fo, "w", stdout);
  }

  inline void insert(int u,
    int v, int _d, int _v)
  {
    Edge *p = new Edge;
    p -> dest = v;
    p -> _d = _d;
    p -> _v = _v;
    p -> next = edge[u];
    edge[u] = p;
  }

  void readdata()
  {
    scanf("%d%d%d", &n, &m, &finish);
    for (int i = 0; i < m; ++i)
    {
      int u, v, _d, _v;
      scanf("%d%d%d%d", &u, &v, &_v, &_d);
      insert(u, v, _d, _v);
    }
  }

  inline void Enq(int Last, int ths, int _v)
  {
    marked[Last].set(ths);
    ++r;
    r &= MOD;
    q[r] = Que(Last, ths, _v);
  }

  inline Que Deq()
  {
    ++f;
    f &= MOD;
    marked[q[f].Last].reset(q[f].ths);
    return q[f];
  }

  void Spfa()
  {
    while (f != r)
    {
      Que Now = Deq();
      int ths = Now.ths;
      int _v = Now._v;
      int Last = Now.Last;
      for (Edge *p = edge[ths]; p; p = p -> next)
      {
        if (p -> _v) _v = p -> _v;
        double t = p -> _d / (double)_v;
        if (dist[Last][ths] + t < dist[ths][p -> dest])
        {
          dist[ths][p -> dest] = dist[Last][ths] + t;
          pre[ths][p -> dest] = Last;
          if (!marked[ths].test(p -> dest))
            Enq(ths, p -> dest, _v);
        }
        _v = Now._v; //要記住每次還原速度。
      }
    }
  }

  void print(int Last, int ths)
  {
    if (ths == 0) return;
    print(pre[Last][ths], Last);
    printf("%d ", Last);
  }

  void work()
  {
    for (int i = 0; i < n; ++i)
     for (int j = 0; j < n; ++j)
      dist[i][j] = MAX_double;
    dist[0][start] = 0;
    Enq(0, start, 70);
    Spfa();
    double Min = MAX_double;
    for (int i = 0; i < n; ++i)
      Min = std::min(Min, dist[i][finish]);
    for (int i = 0; i < n; ++i)
     if (fabs(Min - dist[i][finish]) < zero)
      {print(i, finish); break; }
    printf("%d", finish);
  }

int main()
{
  init_file();
  readdata();
  work();
  exit(0);
}
第二次做:
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <string>
#define min(a, b) ((a) < (b) ? (a) : (b))

const char fi[] = "speed.in";
const char fo[] = "speed.out";
const int maxN = 160, SIZE = 0xfffff;
const double MAX = 1e198, MIN = -MAX;

struct Edge {int v, len, Lim; Edge *next;};
struct Node
{
    int ths, spd, pst; Node() {}
    Node(int pst, int ths, int spd):
        pst(pst), ths(ths), spd(spd) {}
};

Edge *edge[maxN];
Node q[SIZE + 1];
double dist[maxN][maxN];
bool marked[maxN][maxN];
int pre[maxN][maxN], n, m, S, T, f, r;

void init_file()
{
    freopen(fi, "r", stdin);
    freopen(fo, "w", stdout);
    return;
}

inline int getint()
{
    int res = 0; char tmp;
    while (!isdigit(tmp = getchar()));
    do res = (res << 3) + (res << 1) + tmp - '0';
    while (isdigit(tmp = getchar()));
    return res;
}

inline void Ins(int u, int v, int Lim, int len)
{
    Edge *p = new Edge;
    p -> v = v;
    p -> Lim = Lim;
    p -> len = len;
    p -> next = edge[u];
    edge[u] = p;
    return;
}

void readdata()
{
    n = getint(); m = getint(); T = getint();
    for (; m; --m)
    {
        int u = getint(), v = getint(),
        Lim = getint(), len = getint();
        Ins(u, v, Lim, len);
    }
    return;
}

void spfa()
{
    for (int i = 0; i < n; ++i)
    for (int j = 0; j < n; ++j)
        dist[i][j] = MAX;
    memset(pre, 0xff, sizeof pre);
    dist[S][S] = 0; marked[S][S] = 1;
    q[r++] = Node(S, S, 70);
    while (f < r)
    {
        Node Now = q[f++];
        int ths = Now.ths, spd = Now.spd, pst = Now.pst;
        marked[pst][ths] = 0;
        for (Edge *p = edge[ths]; p; p = p -> next)
        {
            int v = (p -> Lim) ? (p -> Lim) : spd;
            double d = (double)(p -> len) / v;
            int nxt = p -> v;
            if (dist[pst][ths] + d < dist[ths][nxt])
            {
                dist[ths][nxt] = dist[pst][ths] + d;
                pre[ths][nxt] = pst;
                if (!marked[ths][nxt])
                {
                    marked[ths][nxt] = 1;
                    q[r++] = Node(ths, nxt, v);
                }
            }
        }
    }
    return;
}

void print(int pst, int ths)
{
    if (pre[pst][ths] == -1)
    {
        printf("%d ", ths);
        return;
    }
    print(pre[pst][ths], pst);
    printf("%d ", ths);
    return;
}

void work()
{
    spfa();
    int pos = T;
    for (int i = 0; i < n; ++i)
    if (dist[i][T] < dist[pos][T])
        pos = i;
    print(pos, T);
    printf("\n");
    return;
}

int main()
{
    init_file();
    readdata();
    work();
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值