poj 1158

题目概述

城市中有N个路口,路口之间有M条双向路,每个路口都有一个信号灯,颜色分蓝(B),紫(P)两种,分别有各自的持续时间,只有当两个路口灯同色时这段路才能通行,一辆车从路口source出发,要到路口target,给定出发时路口灯色,剩余时间,路口蓝灯和紫灯持续时间,问最快多久能到
如果车到路口时刚好变灯,那就得等下一次了

时限

1000ms/3000ms

输入

第一行两个整数source,target,第二行两个整数N,M,其后N行,每行一个字符,代表出发时灯色,三个整数,当前灯剩余时间r,蓝灯持续时间B,紫灯持续时间P,其后M行,每行三个数a,b,c,为路连接的两个路口以及途经用时,输入只有一组

限制

2<=N<=300;1<=M<=14000;1<=r<=B,P<=100;1<=c<=100

输出

每行一个数,为所求最少用时,若无法到达则为0

样例输入

1 4
4 5
B 2 16 99
P 6 32 13
P 2 87 4
P 38 96 49
1 2 4
1 3 40
2 3 75
2 4 76
3 4 77

样例输出

127

讨论

图论,单源最短路,bellman_ford队列优化算法,需要稍微处理一点,在松弛之前算出需要等多长时间,之后松弛,另外需要注意,可能会出现两个路口灯的持续时间都相同,但颜色相反,这种情况下这条路永远没法走,因而当算得等待时间加上途经用时已经无法松弛时,就不用再算了,剩下的部分,就是最短路了
从实现层面上,主要还是处理等待时间,将到达路口的时间加上最初的剩余时间,对灯循环一次的周期求模,由此判断当前灯的状态,若要等待,则等待两个路口最先变灯的时间,然后再计算判断
这个题用的静态邻接表写的,也算比较好的

题解状态

484K,47MS,C++,1699B

题解代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
#define INF 0x3f3
#define MAXN 302
#define memset0(a) memset(a,0,sizeof(a))

int source, target, N, M;//起点 终点 路口数 路数
int r[MAXN], B[MAXN], P[MAXN];//剩余时间 蓝灯持续时间 紫灯持续时间
int to[28005], w[28005], pre[28005], index = 1, al[MAXN], dis[MAXN];//路的终点 途经用时 previous 上一个节点的下标 index 为邻接表提供下标 adjacency_list 邻接表最后一个节点的下标 distance 最短路用时
queue<int>q;//辅助队列
bool inq[MAXN];//辅助数组 在队列中
int fun()
{
    for (int p = 1; p <= N; p++) {
        char ch;
        getchar();//吞掉上一行的换行
        scanf("%c%d%d%d", &ch, &r[p], &B[p], &P[p]);//input
        if (ch == 'B')
            r[p] = B[p] - r[p];//求出当前的灯亮起的时间 这里默认每个周期是先蓝后紫
        else
            r[p] = B[p] + P[p] - r[p];//紫灯则以整个周期扣掉剩余时间
    }
    for (int p = 0; p < M; p++) {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);//input
        to[index] = b, w[index] = c, pre[index] = al[a], al[a] = index++;
        to[index] = a, w[index] = c, pre[index] = al[b], al[b] = index++;
    }
    for (int p = 1; p <= N; p++)//算法初始化
        dis[p] = INF;
    dis[source] = 0;
    q.push(source);
    while (!q.empty()) {
        int a = q.front();
        q.pop();
        inq[a] = 0;
        for (int p = al[a]; p; p = pre[p]) {
            int wait = 0;//等待时间
            while (dis[to[p]] > dis[a] + w[p] + wait) {//为了防止死循环 同样也是剪枝 如果加上等待时间后已经没法松弛就算了
                int timea = (dis[a] + r[a] + wait) % (B[a] + P[a]);//当前路口的灯亮起时间
                int timeb = (dis[a] + r[to[p]] + wait) % (B[to[p]] + P[to[p]]);//下一个路口灯亮起时间
                if ((timea < B[a]) != (timeb < B[to[p]]))//如果灯色不同
                    wait += min(timea < B[a] ? B[a] - timea : B[a] + P[a] - timea, timeb < B[to[p]] ? B[to[p]] - timeb : B[to[p]] + P[to[p]] - timeb);//将最近变灯的时间加到等待时间
                else//同色就不用等了
                    break;
            }
            if (dis[to[p]] > dis[a] + w[p] + wait) {
                dis[to[p]] = dis[a] + w[p] + wait;
                if (!inq[to[p]]) {
                    q.push(to[p]);
                    inq[to[p]] = 1;
                }
            }
        }
    }
    return dis[target] == INF ? 0 : dis[target];//当无法到达的时候就只有INF了 输出0
}
int main(void)
{
    //freopen("vs_cin.txt", "r", stdin);
    //freopen("vs_cout.txt", "w", stdout);

    scanf("%d%d%d%d", &source, &target, &N, &M);//input
    printf("%d\n", fun());//output
}

EOF

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值