距离最短中转站

建物流中转站

牛客网:物流中转站

题目描述

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(ip)+abs(jq)

取整的方案是向上取整还是向下取整。当时做题的时候很困惑,对于牛客给出的示例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))
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值