建物流中转站
题目描述
Shopee物流会有很多个中转站。在选址的过程中,会选择离用户最近的地方建一个物流中转站。 假设给你一个二维平面网格,每个格子是房子则为1,或者是空地则为0。找到一个空地修建一个物流中转站,使得这个物流中转站到所有的房子的距离之和最小。 能修建,则返回最小的距离和。如果无法修建,则返回 -1。
1 若范围限制在100*100以内的网格,如何计算出最小的距离和?
2 当平面网格非常大的情况下,如何避免不必要的计算?
输入描述:
示例1:
4
0 1 1 0
1 1 0 1
0 0 1 0
0 0 0 0
先输入方阵阶数,然后逐行输入房子和空地的数据,以空格分隔。
输出
8
示例2
输入
4
1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1
输出
-1
思路:
距离最短的物流站一定是在建筑群最中心的位置,那么最中心是什么意思?这里我的方案是取每个建筑横纵坐标的均值当做中心位置的坐标。这里要注意,此时的坐标很可能是小数,但不影响,我们接下来只要找到距离这个坐标最近的非建筑点即可。因此我们需要遍历整个矩阵,找出距离中心位置最近的非建筑的坐标,然后将此坐标作为真正的中心位置,再次遍历整个矩阵算距离即可(距离的定义:
a
b
s
(
i
−
p
)
+
a
b
s
(
j
−
q
)
)
abs(i-p) + abs(j-q))
abs(i−p)+abs(j−q))。
取整的方案是向上取整还是向下取整。当时做题的时候很困惑,对于牛客给出的示例1,个人觉得中心点很像
(
1
,
2
)
(1,2)
(1,2) 这个坐标,事实上我们算出来的其实是
(
5
/
6
,
9
/
6
)
(5/6,9/6)
(5/6,9/6) ,也就是
(
0
,
1
)
(0,1)
(0,1) 这个坐标点。也就是说我自己算出来的
(
0
,
1
)
(0,1)
(0,1) 这个坐标点和我自己的直觉中心坐标点
(
1
,
2
)
(1,2)
(1,2) 正好相差
(
1
,
1
)
(1,1)
(1,1) ,在不确定的情况下,我的方案是两个都放入代码中,取结果最小的即可。
事实上我们的直觉往往是有问题的,问题在于我们习惯了把矩阵的行,列索引从1开始计数,而在编程语言里,索引往往是从0开始计数的。比如上述的示例1,总共有六个建筑点,行索引和为5,列索引和为9。假设我们按照自己的直觉,看待矩阵时直接从1开始计数,那么直觉中的行和为11,列和为15,取平均就是
(
11
/
6
,
15
/
6
)
(11/6,15/6)
(11/6,15/6) ,即
(
1
,
2
)
(1,2)
(1,2) 。恰好和我们的直觉吻合,但是这个中心点是基于索引从1开始计数得到的,因此在矩阵中的真实索引应该是
(
0
,
1
)
(0,1)
(0,1),就是我们先前推导的。由于自己的惯性思维,绕了个大弯子,实感惭愧!
def cal_distance(num,k,l): #找出距离理论上的中心位置(k,l)最近的真正中心位置(p,q)并计算总的距离
res = 0
record = 2*n
for i in range(n):
for j in range(n):
if num[i][j]==0:
temp = abs(i-k) + abs(j-l)
if record > temp:
record = temp
p,q = i,j
for i in range(n):
for j in range(n):
if num[i][j] == 1:
res += abs(i-p) + abs(j-q)
return res
if __name__ == '__main__':
n = int(input())
num = [] #存放整个矩阵
for _ in range(n):
num.append(list(map(int,input().split())))
row, col, count = 0, 0, 0
for i in range(n):
for j in range(n):
if num[i][j]==1:
row += i
col += j
count += 1
if count == n**2:
print(-1)
else:
k = row/count
l = col/count #k,l分别是理论上的中心位置的横纵坐标,注意是小数
print(cal_distance(num,k,l))