SGU103 Traffic Lights

SGU103 Traffic Lights

题目大意

给出一张无向图,每个顶点有一盏红绿灯,按照一定的周期变换(左闭右开)
对于某一时刻,若一条边两个端点的颜色相同,则视为可通过,所需时间为边的权值
问0时刻从s出发,最快何时可以到达t

算法思路

不难发现,到达某个点的时间越早越好,可以使用最短路算法求解
对于松弛操作,先找出从dis[u]开始第一次颜色相等的时刻,加上边权后更新dis[v]
不难发现,等待时间最多是一个半周期,否则不存在,粗略的证明如下:

  • 若v的周期与u的周期等长,则结果可在一个周期内判定
  • 否则不妨设v周期短于u,考虑u的第一个周期,假设先绿灯后红灯
  • 若u为绿灯时v出现绿灯,则一个周期内存在相等的时刻
  • 否则u为绿灯时v必定是红灯,而u为红灯时由于周期不等长,v必定出现红灯
  • 此时u的第一个周期还未结束,故一个半周期内必定存在相等的时刻

时间复杂度: SPFA - O(kE200)

代码

/**
 * Copyright (c) 2015 Authors. All rights reserved.
 * 
 * FileName: 103.cpp
 * Author: Beiyu Li <sysulby@gmail.com>
 * Date: 2015-05-21
 */
#include <bits/stdc++.h>

using namespace std;

#define rep(i,n) for (int i = 0; i < (n); ++i)
#define For(i,s,t) for (int i = (s); i <= (t); ++i)
#define foreach(i,c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i)

typedef long long LL;
typedef pair<int, int> Pii;

const int inf = 0x3f3f3f3f;
const LL infLL = 0x3f3f3f3f3f3f3f3fLL;

const int maxn = 300 + 5;
const int maxe = 28000 + 5;

int psz;
struct Edge {
        int v, w;
        Edge *next;
} epl[maxe], *e[maxn];

void add_edge(int u, int v, int w)
{
        Edge *i = epl + psz++;
        i->v = v; i->w = w; i->next = e[u]; e[u] = i;
}

int n, m, s, t;
bool c[maxn];
int r[maxn], l[maxn][3];
int dis[maxn], pre[maxn];
bool inq[maxn];

int color(int u, int now)
{
        now = (now - r[u] + l[u][2]) % l[u][2];
        return now < l[u][c[u]^1]? c[u] ^ 1: c[u];
}

int calc(int u, int v, int now)
{
        For(i,now,now+300) if (color(u, i) == color(v, i)) return i;
        return inf;
}

void spfa()
{
        deque<int> deq;
        memset(dis, 0x3f, sizeof(dis));
        memset(inq, false, sizeof(inq));
        dis[s] = 0; inq[s] = true; deq.push_back(s);
        while (!deq.empty()) {
                int u = deq.front(); deq.pop_front(); inq[u] = false;
                for (Edge *i = e[u]; i; i = i->next) {
                        int v = i->v, w = calc(u, v, dis[u]) + i->w;
                        if (dis[v] <= w) continue;
                        dis[v] = w; pre[v] = u;
                        if (inq[v]) continue; inq[v] = true;
                        deq.empty() || dis[v] < dis[deq.front()]?
                                deq.push_front(v): deq.push_back(v);
                }
        }
}

void print(int u)
{
        if (u == s) { printf("%d", u + 1); return; }
        print(pre[u]); printf(" %d", u + 1);
}

int main()
{
        scanf("%d%d", &s, &t);
        --s; --t;
        scanf("%d%d", &n, &m);
        rep(i,n) {
                char buf[8];
                scanf("%s%d%d%d", buf, &r[i], &l[i][0], &l[i][1]);
                c[i] = (buf[0] == 'P');
                l[i][2] = l[i][0] + l[i][1];
        }
        while (m--) {
                int u, v, w;
                scanf("%d%d%d", &u, &v, &w);
                --u; --v;
                add_edge(u, v, w);
                add_edge(v, u, w);
        }
        spfa();
        if (dis[t] == inf) puts("0");
        else printf("%d\n", dis[t]), print(t), puts("");

        return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值