有州的旅行推销员

时间:2014-03-14 05:32:45

标签: algorithm language-agnostic traveling-salesman

我们说我们有一个有向图。我们希望通过在此图的边缘移动来访问每个节点一次。每个节点都注有一个或多个标签;一些节点可以共享标签,甚至具有完全相同的标签集。随着我们的行走,我们正在收集我们遇到的每个不同标签的列表 - 我们的目标是找到尽可能推迟获取新标签的步行。

作为一个旅行者类比重申这一点,让我们说地毯推销员正在试图决定他应该从哪个供应商那里获得他的地毯。他列出了该市所有地毯工厂的清单。他与每家工厂预约,并收集他们制作的地毯样品。

假设我们有3家工厂,生产以下种类的地毯:

F1: C1, C2, C3
F2: C1, C4
F3: C1, C4, C5

推销员可以采取以下路线:

  1. 从F1开始,收集C1,C2,C3。转到F2,收集C4(因为他已经有C1)。转到F3,收集C5(他已经有C1和C4)。
  2. 从F1开始,收集C1,C2,C3。转到F3,收集C4和C5。去F2,什么都不收集(因为事实证明他已经有了所有的地毯)。
  3. 从F2开始,收集C1,C4。转到F1,收集C2,C3。转到F3并收集C5。
  4. 从F2开始,收集C1,C4。转到F3,收集C5。转到F1并收集C3。
  5. 从F3开始,收集C1,C4,C5。转到F1,收集C2,C3。转到F2,什么都不收。
  6. 从F3开始,收集C1,C4,C5。转到F2,什么都不收。转到F1,收集C2,C3。
  7. 请注意,销售员有时会去工厂,即使他知道他已经为他们生产的每种地毯收集了样品。这个类比在这里分解了一下,但是让我们说他必须去拜访他们,因为没有出席他的约会是不礼貌的。

    现在,地毯样品很重,我们的推销员正在徒步旅行。距离本身并不是非常重要(假设每个边缘的成本都是1),但他并不想随身携带一大堆样本。因此,他需要计划他的行程,以便他去最后一次访问那些有很多稀有地毯的工厂(并且他将不得不去买很多新的样品)。

    对于上面的示例路径,以下是旅程每一段(第2-4列)和总和(第5列)的样本数量。

    1    0    3    4    7
    2    0    3    5    8
    3    0    2    4    6
    4    0    2    3    5
    5    0    3    5    8
    6    0    3    3    6
    

    我们现在可以看到路线2非常糟糕:首先他必须从F1到F3携带3个样本,然后他必须携带从F3到F2的5个样本!相反,他本可以选择路线4 - 他会先从F2到F3携带2个样本,然后从F3到F1再进行3个样本。

    另外,如最后一栏所示,每个边缘的样本总和是一个很好的指标,表明他必须携带多少样本:他携带的样本数量不会减少,所以提前访问各个工厂必然会通过访问地毯很少的类似工厂来实现低额贷款。

    这是一个已知问题吗?有算法可以解决吗?

    注意:我建议小心根据我的示例问题做出假设。我当场想出了它,故意为了简洁而保持小。可以肯定的是,有许多边缘情况无法捕获。

2 个答案:

答案 0 :(得分:2)

由于图表的大小很小,我们可以考虑使用位掩码和动态编程来解决这个问题(类似于我们如何解决旅行商问题)

假设我们共有6个城市可供参观。因此,起始状态为0,结尾为111111b或十进制为127.

从每个步骤开始,如果状态为x,我们可以轻松计算销售人员携带的抽样数量,从状态x到状态y的成本将是从x到y 的新添加样本的数量时间未访问城市的数量。

 public int cal(int mask) {
    if (/*Visit all city*/) {
        return 0;
    }

    HashSet<Integer> sampleSet = new HashSet();//Store current samples
    int left = 0;//Number of unvisited cities
    for (int i = 0; i < numberOfCity; i++) {
        if (((1 << i) & mask) != 0) {//If this city was visited
            sampleSet.addAll(citySample[i]);
        } else {
            left++;
        }
    }
    int cost;
    for (int i = 0; i < numberOfCity; i++) {
        if (((1 << i) & mask) == 0) {
            int dif = number of new sample from city i;
            cost = min(dif * left + cal(mask | (1 << i));

        }
    }
    return cost;
}

答案 1 :(得分:0)

在每对节点之间存在边缘的情况下,并且每个地毯仅在一个位置可用,这看起来易于处理。如果您在有Y步骤时拿起X地毯,那么从此到最终成本的贡献是XY。因此,您需要最小化SUM_i XiYi,其中Xi是当您有Yi步骤时拾取的地毯数量。您可以通过访问工厂按照该工厂提取的地毯数量的增加顺序来完成此操作。如果你提供一个时间表,你在A比B拿起更多的地毯,并且你在B之前访问A,我可以通过交换你访问A和B的时间来改进它,所以任何不符合这个规则的时间表是不是最佳的。