带信号灯的最短路dijkstra问题(阿里巴巴2018校园招聘算法题)

题目描述

现在城市有N个路口,每个路口有自己的编号,从0到N-1,每个路口还有自己的交通控制信号,例如0,3表示0号路口的交通信号每3个时刻变化一次,即0到3时刻0号路口允许通过,3到6时刻不允许通过,而6到9时刻又允许通过;以此类推,所有路口的允许通行都从时刻0开始。同时城市中存在M条道路将这N个路口相连接起来,确保从一个路口到另一个路口都可达,每条路由两个端点加上通行所需要的时间表示。现在给定起始路口和目的路口,从0时刻出发,请问最快能在什么时刻到达?

思路

由于Dijkstra算法每一步计算的都是从初始点到达各点之间的最短距离,这使得我们能够很容易计算在有信号灯的情况下通过路口需要在路口等待信号灯的时间,因此加入信号灯约束之后可对Dijkstra算法做如下改动即可:给定一个网络有节点集V,有向边集E,权重矩阵W,起点和终点分别为s和t,各路口信号灯变化周期为time。

初始化:标记节点集合为M,仅包含s;M中节点到V-M中节点的距离设为无穷大,初始节点到各节点的最短时间径记为dis,dis[s]=0;

step1:计算从M中各节点到V-M中各节点通行时间最小值,找到V-M中对应最短通行时间的节点并将其添加为标记节点,对应的最短通行时间为找到的最小值。

松弛:dis[j]= min{dis[j],  dis[i] + W[i, j] + wait[i, j]},其中wait为从i路口到通过j路口需要等待信号灯时间,计算方法为:如果j为终节点则等待时间为0[不用通过,到达即可]。

否则计算wait= (dis[i] + W[i, j])%(2*time[j]),如果wait小于time[j]则无需等待直接通过,否则需等待2*time[j]-wait。

step2:检查标记节点中是否包含t,如果是则终止,否则进入step1。

输入:

节点总数,节点的等待时间,边数,两个路口间的通行时间,s,t

9
0,3
1,5
2,7
3,3
4,5
5,7
6,9
7,3
8,5
14
0,1,4
0,7,8
1,2,8
1,7,11
2,3,7
2,5,4
2,8,2
3,4,9
3,5,14
4,5,10
5,6,2
6,8,6
6,7,1
7,8,7
0,4

输出:28

程序:

#include<stdio.h>
#include<string.h>
#include<string>
#include<iostream>
#include<algorithm>
using namespace std;

#define ms(x, y) memset(x, y, sizeof(x))
#define mc(x, y) memcpy(x, y, sizeof(x))
const int inf = 0x3f3f3f3f;
const int N = 50;
int tm[N], dis[N], road[N][N];
bool vis[N];
int n;

int dijkstra(int s, int t)
{
	ms(vis, false);
	ms(dis, inf);
	dis[s] = 0;
	while (true)
	{
		int v = -1;
		for (int i = 0; i < n; i++)
		if (!vis[i] && (v == -1 || dis[v]>dis[i])) v = i;
		vis[v] = true;
		for (int i = 0; i < n; i++)
		{
			if (!vis[i])
			{
				int wait = (dis[v] + road[v][i]) % (tm[i] * 2);
				if (wait < tm[i])wait = 0;   //不需等待
				else wait = tm[i] * 2 - wait;  //等待时间
				if (i == t) wait = 0;  //如果遇到终节点则无需等待
				if (dis[i] > dis[v] + road[v][i] + wait) // 松弛操作的变型
					dis[i] = dis[v] + road[v][i] + wait;
			}
		}
		if (v == t)break; // 终节点退出
	}
	return dis[t];
}

int main()
{
	scanf("%d", &n);
	for (int i = 0; i < n; i++)
	{
		int pos, time;
		scanf("%d,%d", &pos, &time);
		tm[pos] = time;
	}
	int m;
	scanf("%d", &m);
	ms(road, inf);
	for (int i = 0; i < m; i++)
	{
		int from, to, need;
		scanf("%d,%d,%d", &from, &to, &need);
		road[from][to] = need;
		road[to][from] = need;
	}
	int s, t;
	scanf("%d,%d", &s, &t);
	printf("%d\n", dijkstra(s, t));
	return 0;
}
posted @ 2017-09-04 10:42  demianzhang  阅读(487)  评论(0编辑  收藏  举报