您的位置:首页 > 编程语言

阿里巴巴2018校园招聘运筹优化算法工程师编程题

2017-08-26 10:20 441 查看
对于考试向来都是后知后觉,过后留下一堆不甘与遗憾——然而不行就是不行,再接再厉。

声明:涉及阿里校招笔试,如有侵权,请联系我删除。

一、带信号灯的最短路问题

1. 题目描述

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

2. 分析思路

如果没有信号灯。

要在网络中找到从 一点到另外一点的最短路径,我们可以使用现有的最短路算法,可参考http://blog.csdn.net/murmured/article/details/19281031

在加入信号灯约束之后,要考虑到达中间某个路口后的等待时间,而等待时间与到达路口的时刻和路口信号灯变化周期有关。如果是按照Floyd算法来看,需要遍历每一个节点k,如果d[i, j] > d[i, k] + d[k, j]则更新d[i, j]。但是每一个d[i, j]都是考虑以i为起点j为终点计算的通行时间,所以应比较d[i, j]与d[i, k]+w[k, j]+从通过k路口后时刻算起到达j路口的时间。由此看来Floyd算法在本题行不通。

再考虑Dijkstra算法。题目要求计算的是从给定起点到终点的最短时间,这与Dijkstra算法计算其实节点到终节点的特性类似。现在回顾一下Dijkstra算法:

给定一个网络有节点集V,有向边集E,权重矩阵W,起点和终点分别为s和t。

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

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

step2:计算从M中各节点到V-M中各节点距离的最小值,找到V-M中对应最小值的节点并将其添加为标记节点,对应的最短距离为找到的最小值。用i遍历M中各节点,j遍历V-M中各节点,min = min{T[i] + W[i, j]}。

由于Dijkstra算法每一步计算的都是从初始点到达各标记几点之间的最短距离,这使得我们能够很容易计算在有信号灯的情况下通过路口需要在路口等待信号灯的时间,因此加入信号灯约束之后可对Dijkstra算法做如下改动即可:

给定一个网络有节点集V,有向边集E,权重矩阵W,起点和终点分别为s和t,各路口信号灯变化周期为C。

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

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

step2:计算从M中各节点到V-M中各节点通行时间最小值,找到V-M中对应最短通行时间的节点并将其添加为标记节点,对应的最短通行时间为找到的最小值。用i遍历M中各节点,j遍历V-M中各节点,min = min{T[i] + W[i, j] + waittime[i, j]},其中waittime为从i路口到通过j路口需要等待信号灯时间,计算方法为:如果j为终节点则等待时间为0,否则计算m = (T[i] + W[i, j])%2*C[j],如果m小于C[j]则无需等待直接通过,否则需等待2C[j]-m。

3. 代码实现

# -*- coding: UTF-8 -*-.
#!/bin/python
import numpy as np

def minTravelTime(N, intersections, M,roads, s, t):
# 初始化
# 初始点到各点的最短时间以及通过该路口需要等待的时间
dist = [float("inf") for i in range(N)]
# 初始点s到自己的距离为0
dist[s] = 0
# 最开始标记节点为初始节点
markNodes = [s]
wt = lambda x, y: 0 if divmod(x, 2*y)[1] < y else 2*y - divmod(x, 2*y)[1]
while t not in markNodes:
tmp_min = float("inf")
tmp_node = -1
for i in markNodes:
for j in range(N):
if j not in markNodes:
if j == t:
wait_time = 0
else:
wait_time = wt(dist[i] + roads[i, j], intersections[j])
if dist[i] + roads[i, j] + wait_time <= tmp_min:
tmp_node = j
tmp_min = dist[i] + roads[i, j] + wait_time
markNodes.append(tmp_node)
dist[tmp_node] = tmp_min
return dist[t]

_N = int(input())     # 路口总数
_intersections = {}   # 路口信号灯时间间隔
for i in range(0, _N):
x, y = input().split(',')
_intersections[int(x)-1] = int(y)

_M = int(input())
_roads = np.zeros((_N, _N))
for j in range(0, _M):
u, v, w = input().split(',')
_roads[int(v), int(u
bbae
)] = int(w)
_roads[int(u), int(v)] = int(w)

for i in range(_N):
for j in range(_N):
if(_roads[i, j]) == 0:
_roads[i, j] = float("inf")
print(_roads)
# 输出任意起点到终点的最短到达时间
for i in range(_N):
for j in range(_N):
if i != j:
print(i+1, j+1, minTravelTime(_N, _intersections, _M, _roads, i, j))

# 以下为题目要求输出
# _s, _t = input().split(',')
# _s, _t = int(_s), int(_t)
# print(str(minTime)+"\n")


4. 算例演示

(1)输入

6

0,16

1,17

2,15

3,17

4,18

5,14

9

0,1,66

0,2,80

0,3,50

1,4,78

1,5,90

1,2,30

2,3,45

2,5,81

4,5,76

(2)输出

1 2 66.0

1 3 80.0

1 4 50.0

1 5 146.0

1 6 158.0

2 1 66.0

2 3 30.0

2 4 75.0

2 5 78.0

2 6 90.0

3 1 80.0

3 2 30.0

3 4 45.0

3 5 112.0

3 6 81.0

4 1 50.0

4 2 90.0

4 3 45.0

4 5 180.0

4 6 141.0

5 1 144.0

5 2 78.0

5 3 108.0

5 4 165.0

5 6 76.0

6 1 168.0

6 2 90.0

6 3 81.0

6 4 135.0

6 5 76.0

二、仓库货架储位编号

1. 题目描述

菜鸟仓库的货架格子编号满足如下规律:第1个格子编号为1,第2-3个12,第4-6个格子编号为123……以此类推每个格子由一个0-9之间的整数编号,n个格子的编号连起来有如下规律1|12|123|……|123456789101112131415|……|12345678910111213141516…k,仓库中的储位一共有一千多万个。如何快速找到第K个格子对应的编号。

2. 分析思路

乍一看,以为第n行第k个格子的编号为k,然后求和S=1+2+3+…+n的和,即为前n行的格子数,再用K-S即为k的编码。其实然并卵,注意题目描述,“每个格子由一个0-9之间的整数编号”,例如12345678910这个序列中,第10个格子的编号为1,第11个格子的编号为0,所以乍一看的思路不对。

再细想,为了确定第K个格子的编号,有以下几点至关重要:一是格子的行号n,二是1+2+3+…+k共有多少个格子f(k),四是前n-1行一共有多少个格子snp。如果能找出f(k)的通项公式,然后对f(k)求前n项和,再看K位与那个区间内得到snp,再计算比K-snp小的f(k)和比K-snp大或相等的f(k+1),得到k,则K的编号即为k+1从左边数第K-snp-f(k)位对应的数。所以现在问题的关键变成计算一个给定的数有多少位。这个问题经过一番苦思冥想终究没想到怎么样用一个方便求和的通项公式表达,但是在程序里面用函数来时先却是非常的简单,在Python里面可以表达成len(str(k))。所以在这里思路一转,变成牺牲计算时间用迭代的方式进行求解,过程如下:

G(n):前n行格子的总数,f(n):第n行格子的总数

step1:n = 1, G(n) = 0;

step2:G(n) = G(n) + f(n),比较G(n)和K的大小,如果G(n)小于K,则n = n + 1,重复step2;否则返回n和snp = G(n)-f(n),分别是K位于的行号n和前n-1行格子数,k = 1;

step3:比较f(k)与K-snp的大小,如果f(k)小于K-snp则k = k + 1继续step3,否则返回k;

step4:K位于第n行的第k个数中的第K-snp-f(k-1)位数。

f(n)的计算方式如下:

def f(n):
if n == 0:
return 0
else:
return reduce(lambda x, y: x+y, list(map(lambda x: len(str(x)), range(1, n+1))))


3. 代码实现

# -*- coding: UTF-8 -*-.
#!/bin/python
from functools import reduce

# 第n行格子数
def f(n): if n == 0: return 0 else: return reduce(lambda x, y: x+y, list(map(lambda x: len(str(x)), range(1, n+1))))

def P(K):
n = 1
gn = f(n)
while gn < K:
n += 1
gn = gn + f(n)
# print(gn)
snp = gn - f(n)

k = 1
while f(k) < K-snp:
k += 1
return [n, list(str(k))[K-snp-f(k-1)-1]]

ts = ''
tn = 1
for i in range(1, 1000):
[n, p] = P(i)
if n == tn:
ts += str(p)
else:
print(ts)
tn = n
ts = str(p)
print(ts)


4. 算例演示

(1)输入:1-1000

(2)输出

1

12

123

1234

12345

123456

1234567

12345678

123456789

12345678910

1234567891011

123456789101112

12345678910111213

1234567891011121314

123456789101112131415

12345678910111213141516

1234567891011121314151617

123456789101112131415161718

12345678910111213141516171819

1234567891011121314151617181920

123456789101112131415161718192021

12345678910111213141516171819202122

1234567891011121314151617181920212223

123456789101112131415161718192021222324

12345678910111213141516171819202122232425

1234567891011121314151617181920212223242526

123456789101112131415161718192021222324252627

12345678910111213141516171819202122232425262728

1234567891011121314151617181920212223242526272829

123456789101112131415161718192021222324252627282930

12345678910111213141516171819202122232425262728293031

1234567891011121314151617181920212223242526272829303132

123456789101112131415161718192021222324252627282930313233

12345678910111213141516171819202122232425262728293031323334

1234567891011121314151617181920212223242526272829303132333435

123456789101112131
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息