阿里巴巴全球数学竞赛第二题——python

阿里巴巴全球数学竞赛第二题——python

题目

附图中有一个无向图,其中圈内数字代表一个地点,边e上数字代表长度Le(双向相同)。
一位外卖小哥在起点A,要去3个商家(B1, B2, B3)取餐,送到3个对应的地方(C1, C2, C3),即B1至C1,B2至C2,B3至C3。
小哥的电动助力车的箱子同时最多装下2份外卖。

请问: 小哥该怎么走最短路径?这个最短路径的长度是多少?
这里,A是出发点,最后一餐(不限次序)送达地为终点。
为了简化问题,假设商家已经备好了外卖,小哥取餐送餐不用等。又假设每份外卖重量大小一样。

附图: 在这里插入图片描述

解答

import copy


points_graph = {
    '1': {
        '2': 1,
        '5': 1
    },
    '2': {
        '1': 1,
        '3': 2,
        '6': 2
    },
    '3': {
        '2': 2,
        '4': 1,
        '8': 2
    },
    '4': {
        '3': 1,
        '15': 3
    },
    '5': {
        '1': 1,
        '6': 1,
        '9': 1
    },
    '6': {
        '2': 2,
        '5': 1,
        '7': 1
    },
    '7': {
        '6': 1,
        '8': 1,
        '10': 1
    },
    '8': {
        '3': 2,
        '7': 1,
        '11': 1
    },
    '9': {
        '5': 1,
        '10': 3,
        '12': 2
    },
    '10': {
        '7': 1,
        '9': 3,
        '11': 1,
        '13': 2
    },
    '11': {
        '8': 1,
        '10': 1,
        '14': 1
    },
    '12': {
        '9': 2,
        '13': 2
    },
    '13': {
        '10': 2,
        '12': 2,
        '14': 1
    },
    '14': {
        '11': 1,
        '13': 1,
        '15': 1
    },
    '15': {
        '4': 3,
        '14': 1
    },
}


b_points = {
    '3': 'B1',
    '7': 'B2',
    '4': 'B3'
}

c_points = {
    '12': 'C1',
    '11': 'C2',
    '13': 'C3'
}

most_orders = 2


def point_travel(point_current, distance_current, orders_get, orders_finish, travel_history):
    """
    point_current: 所在位置, 
    distance_current: 已走路径距离, 
    orders_get: 手中有什么外卖,     
    orders_finish: 已完成订单, 
    travel_history: 路径集
    """
    # 订单是否都已完成
    if len(orders_finish) == len(c_points):
        if distance_current in distance_travel:
            distance_travel[distance_current].append(travel_history)
        else:
            distance_travel[distance_current] = [travel_history]
        return None
    # 当计算完一个distances时,只考虑比当前distances小的走法
    if len(distance_travel) > 0:
        if distance_current >= min(distance_travel.keys()) - 1:
            return None
    # next step
    for key, value in points_graph[point_current].items():
        # 当next step点的状态已存在时,continue
        if (key, orders_get, orders_finish) in travel_history:
            continue
        distances_ = distance_current
        distances_ += value
        travel_history_ = travel_history.copy()
        orders_get_ = orders_get.copy()
        if key in b_points:
            # 当前为商家
            if len(orders_get) < most_orders and b_points[key] not in orders_get and b_points[key].replace('B', 'C') not in orders_finish:
                # 在商家点,当箱子未满时且当前商家外卖未装箱且当前商家订单未完成,小哥可选是否将外卖装箱
                for i in range(2):
                    travel_history_ = travel_history.copy()
                    if i:
                        orders_get_.add(b_points[key])
                    travel_history_.append((key, orders_get_, orders_finish))
                    point_travel(key, distances_, orders_get_, orders_finish, travel_history_)
            else:
                # 当前商家不装箱
                travel_history_.append((key, orders_get_, orders_finish))
                point_travel(key, distances_, orders_get_, orders_finish, travel_history_)
        elif key in c_points:
            # 当前为客户
            order = c_points[key].replace('C', 'B')
            orders_finish_ = orders_finish.copy()
            # 有当前客户的外卖,就结束订单
            if order in orders_get:
                orders_get_.remove(order)
                orders_finish_.add(c_points[key])
            travel_history_.append((key, orders_get_, orders_finish_))
            point_travel(key, distances_, orders_get_, orders_finish_, travel_history_)
        else:
            # 更新路径
            travel_history_.append((key, orders_get, orders_finish))
            # 转入下一个位置动作
            point_travel(key, distances_, orders_get, orders_finish, travel_history_)
    return None


if __name__ == '__main__':
    distance_travel = {}
    point_travel('2', 0, set(), set(), [])
    min_distance = min(distance_travel.keys())
    print(min_distance)
    for travel in distance_travel[min_distance]:
        print(travel)

16
16
[('6', set(), set()), ('7', {'B2'}, set()), ('8', {'B2'}, set()), ('11', set(), {'C2'}), ('8', set(), {'C2'}), ('3', {'B1'}, {'C2'}), ('4', {'B1', 'B3'}, {'C2'}), ('15', {'B1', 'B3'}, {'C2'}), ('14', {'B1', 'B3'}, {'C2'}), ('13', {'B1'}, {'C2', 'C3'}), ('12', set(), {'C1', 'C2', 'C3'})]
[('6', set(), set()), ('7', {'B2'}, set()), ('10', {'B2'}, set()), ('11', set(), {'C2'}), ('8', set(), {'C2'}), ('3', {'B1'}, {'C2'}), ('4', {'B1', 'B3'}, {'C2'}), ('15', {'B1', 'B3'}, {'C2'}), ('14', {'B1', 'B3'}, {'C2'}), ('13', {'B1'}, {'C2', 'C3'}), ('12', set(), {'C1', 'C2', 'C3'})]
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值