旅行推销员(只需要访问节点子集):Bugged

时间:2012-11-02 12:17:17

标签: algorithm graph-theory shortest-path

我的旅行销售人员问题代码需要帮助。它的错误......我知道,因为它是一个学校作业,有测试用例。所以这就是它。

给定连接图,我需要访问节点的子集。如何计算最短路径?

enter image description here

例如,请参阅上图。我需要从0开始并访问一些/所有节点然后返回到零。在这个过程中,我需要计算最短路径。

假设我需要访问所有节点,我将从0 -> 1 -> 2 -> 3 -> 0 = 20 + 30 + 12 + 35 = 97开始。假设现在我只需要访问节点2,我将从0 -> 3 -> 2 -> 3 -> 0开始,因为它给出了最短路径94(我可以访问节点,如果它可以提供最短路径,我不必访问它)。

基本上,我做了:

  1. 计算任意两对所需节点与源(0)之间的最短路径。这给了我一个最短的路径2D表(我使用了dijkstra的):

      |  0  1  2  3
    --+--------------
    0 | 
    1 |
    2 | 
    3 |
    
  2. 现在,我修改了购物销售人算法(又名Floyd Warshall或APSP)来使用此表。当前的Java源代码(TSP和dijkstra)看起来像:

    int TSP(int source, int visited) {
       if (visited == (int)(Math.pow(2, K)-1)) { // all required visited
        return sssp.get(source).get(0); // return to source (0)
      } else if (memo.containsKey(source) && memo.get(source).containsKey(visited)) {
        return memo.get(source).get(visited);
      } else {
        int item;
        if (!memo.containsKey(source)) {
          memo.put(source, new HashMap<Integer, Integer>());
        }
        memo.get(source).put(visited, 1000000);
        for (int v = 0; v < K; v++) {
          item = shoppingList[v];
          if (!hasVisited(visited, item)) {
            memo.get(source).put(visited, Math.min(
              memo.get(source).get(visited),
              sssp.get(source).get(item) + TSP(item, visit(visited, v))
            ));
          }
        }
        return memo.get(source).get(visited);
      }
    }
    
    int dijkstra(int src, int dest) {
      PriorityQueue<IntegerPair> PQ = new PriorityQueue<IntegerPair>();
      HashMap<Integer, Integer> dist = new HashMap<Integer, Integer>(); // shortest known dist from {src} to {node}
      // init shortest known distance
      for (int i = 0; i < N+1; i++) {
        if (i != src) {
          dist.put(i, Integer.MAX_VALUE); // dist to any {i} is big(unknown) by default
        } else {
          dist.put(src, 0); // dist to {src} is always 0
        }
      }
      IntegerPair node;
      int nodeDist;
      int nodeIndex;
    
      PQ.offer(new IntegerPair(0, src)); 
      while (PQ.size() > 0) {
        node = PQ.poll();
        nodeDist = node.first();
        nodeIndex = node.second();
    
        if (nodeDist == dist.get(nodeIndex)) {
          // process out going edges
          for (int v = 0; v < N+1; v++) { // since its a complete graph, process all edges
            if (v != nodeIndex) { // except curr node
              if (dist.get(v) > dist.get(nodeIndex) + T[nodeIndex][v]) { // relax if possible
                dist.put(v, dist.get(nodeIndex) + T[nodeIndex][v]);
                PQ.offer(new IntegerPair(dist.get(v), v));
              }
            }
          }
        }
      }
      return dist.get(dest);
    }
    
    1. visited用作位掩码以指示是否已访问节点
    2. ssspHashMap<Integer, HashMap<Integer, Integer>>,其中第一个hashmap的键是源节点,第二个hashmap的键是目标。所以它基本上代表你在第1点看到的2D表。
    3. memo正是我在动态编程中用作给定访问位图的先前计算的来自节点的最短路径的“缓存”。
  3. 完整来源: http://pastie.org/5171509

    通过的测试用例:

    1
    
    3 3
    1 2 3
    0 20 51 35
    20 0 30 34
    51 30 0 12  
    35 34 12 0 
    

    第一行是测试用例的数量。第3行(3 3)。第一个3是节点数,第二个3是所需节点数。第4行是所需节点的列表。然后剩下的是边权重表。

    失败的测试用例是:

    9 9
    1 2 3 4 5 6 7 8 9
    0 42 360 335 188 170 725 479 359 206
    42 0 402 377 146 212 767 521 401 248
    360 402 0 573 548 190 392 488 490 154
    335 377 573 0 293 383 422 717 683 419
    188 146 548 293 0 358 715 667 539 394
    170 212 190 383 358 0 582 370 300 36
    725 767 392 422 715 582 0 880 704 546
    479 521 488 717 667 370 880 0 323 334
    359 401 490 683 539 300 704 323 0 336
    206 248 154 419 394 36 546 334 336 0
    

    我得到3995但答案是2537 ...抱歉我知道这很难调试......我遇到同样的问题,测试用例太大了......至少对于人类......所以我我正在创建一个较小的测试用例进行测试,但它们似乎已经通过......

1 个答案:

答案 0 :(得分:2)

也许不是一个完整的答案,但我认为它至少指向了正确的方向:你的代码似乎给出了跟随路径的结果0-&gt; 1-&gt; 2-&gt; ...-&gt; N - 大于0。似乎没有真正的优化。

我重新编写了一些代码以获得一个小的失败测试用例:

int[][]mat=new int[N+1][N+1];
//original
//mat[0]=new int[]{0,20,51,35};
//mat[1]=new int[]{20,0,30,34};
//mat[2]=new int[]{51,30,0,12};
//mat[3]=new int[]{35,34,12,0};
//switched order of nodes, node 2 is now node 1
mat[0]=new int[]{0,51,20,35};
mat[1]=new int[]{51,0,30,12};
mat[2]=new int[]{20,30,0,34};
mat[3]=new int[]{35,12,34,0};

这产生146作为最佳路径,表明它遵循路径0-> 1-> 2-> 3-> 0(47 + 30 + 34 + 35,47是最短路径0到1使用节点4)(所有节点号都与我的订单开关一起)。

编辑:我又一次快速找到了罪魁祸首。您有一行if (!hasVisited(visited, item))来检查您是否已访问过节点item。但是,访问权限由visit(visited, v)构建,其中vshoppinglist的索引。 item =shoppinglist[v]但是如果您移动了访问过的矢量,则应该使用相同的内容。

您应该使用if (!hasVisited(visited, v))代替if (!hasVisited(visited, item))

在一个不相关的说明中,我不确定找到最短路径的第一步是否必要或是否会影响您的结果。如果从A到B的直接链接比通过其他节点(比如C)更长,那么它将在距离表中被替换。如果您在最终解决方案中使用该链接从A到B,那么您实际上将通过C进行,这已经在您的路径中(因为该路径是完整的TSP解决方案)。如果一个节点只能访问一次,那么这可能是个问题。