同步购物HackerRank

时间:2017-07-24 11:21:53

标签: java algorithm graph-algorithm dijkstra

我在HackerRank上尝试了这个Synchronous Shopping问题,我不知道如何处理它。所以我看了社论,我很困惑。也许我误解了Dijkstra的单源最短路径算法是如何工作的。

这取自editorial

他说

  

到州D(V, B)的最短距离表示所需的最短时间   用购买的面具V中的鱼来访问购物中心B

然后他描述了我们可能从一种状态转移到另一种状态的两种可能方式,之后他说

  

计算所有最小时间后....

我认为他的意思是,在我们到达节点 N 之后,我们应该考虑所有可能的获取鱼的方法。所有2 ^ k方式。比如,我们考虑

1)当我们到达节点N时,我们只有第一条鱼

2)我们只有第二条鱼......

3)我们只有第一条鱼和第二条鱼..

等。

但是如果我们运行Dijkstra,它将计算从节点1到节点N的最短路径,但是我们必须经过一个特定的路径才能从节点1到达节点N.我们只能获得沿这些节点可用的鱼。我们如何计算所有其他状态? (到达节点N与不同的鱼群)

2 个答案:

答案 0 :(得分:3)

让我试着帮助你,就像我最近做的那样。我将尝试逐个构建解决方案或主要想法。

问题摘要:有两只猫,他们从购物1开始直到N.在每次购物时他们都会购买一套鱼,如果两只猫都购买了所有的鱼,他们只能停在N.两只猫都可以走不同的路,重新回到购物中心。

问题的目的是找到购买所有鱼类的最短时间,这是两只猫到达N之间的最长时间,两只猫一起买了所有可用的鱼。

现在,让我们尝试构建解决方案:想象一下,你没有鱼,你只想知道一只猫从购物1到N的最短时间.Dijkstra可以解决这个问题,对吧?它将计算从源(商店1)到所有其他节点的所有最短时间。

下一步是添加鱼类:现在,假设我们只有一只猫,我们必须在每个站点购买鱼类。试着想象以下内容:而不是图表的每个顶点只是购物中心,而是购物和猫到目前为止购买的鱼类的组合,存储信息。

这意味着购物可以多次添加到优先级队列,这在“普通”Dijkstra算法中不会发生。这允许您计算从源和所有可能组合的最短时间,以达到购买鱼类的购物。不幸的是,这会将顶点数增加到O(N * 2 ^(k-1))。确保存储所有顶点的最短时间,我们将在下一步中使用它。

另一个基本技巧是将鱼类保持在bitset而不是列表或向量中,因为购买鱼类是一种OR操作。使用的大多数按位运算都是常数O(1)。

最后一步是添加第二只猫:如果您按照下面的说明计算Dijkstra,您将获得所有顶点的最短时间,这是所有购物和购买鱼类的状态。我们只对我们在N店的情况感兴趣,并且由猫1和2购买的鱼的组合等于所购买的所有鱼。

已经考虑到的是,如果一只猫到达N,它必须等待另一只猫到达,这意味着无论一只猫买的鱼是什么,如果其他的到达,如果剩下的,那很好。

从技术上讲,您将处理购物N可用的所有可能情景,如果猫1和2的鱼类组合等于所购买的所有鱼类,您将获得最短的时间(所有可能性中的最小值)。 不幸的是,这是一种详尽的方法,但很好。这是Dijkstra处理后堆叠的两个。

答案 1 :(得分:0)

  1. 为简化问题,让我们考虑只有1猫。然后,对于每种鱼类组合,我们都可以为猫计算出到达目的地n的最佳时间。这是可行的,因为鱼的种类数量有限,因此只有2^10种组合。
  2. 现在,由于找到了所有可能的组合,因此2只猫将到达目的地n,其中一只猫。但是,为了找到两只猫的最佳时间,我们应该检查包含所有产品类型的每对配置,并选择最短的时间。
  3. 实际上,使用这种方法,我们可以找到任意数量的猫的解决方案-唯一的不同是最后for个循环的数量(当寻找最短时间时)。对于3只猫,3,对于4只猫,4,等等。

现在,代码:

public class Solution {
    private static final class Shop {
        private final int shopId;
        private final int time;

        public Shop(int shopId, int time) {
            this.shopId = shopId;
            this.time = time;
        }
    }

    private static final class ShopAssortment {
        private final int shopId;
        private final int assortment;

        public ShopAssortment(int shopId, int assortment) {
            this.shopId = shopId;
            this.assortment = assortment;
        }
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String[] nmk = scanner.nextLine().split("\\s+");
        int n = Integer.parseInt(nmk[0]);
        int m = Integer.parseInt(nmk[1]);
        int k = Integer.parseInt(nmk[2]);

        int[] fishTypesAtShop = new int[n + 1];
        for (int i = 1; i <= n; i++) {
            String[] ta = scanner.nextLine().split("\\s+");
            int numOfFishTypes = Integer.parseInt(ta[0]);

            // encode fish types into an integer 
            for (int j = 1; j <= numOfFishTypes; j++) {
                int type = Integer.parseInt(ta[j]) - 1;
                fishTypesAtShop[i] |= (1 << type);
            }
        }

        // initialise connections map so that key denotes a shop and value - list of shops connected to it
        Map<Integer, List<Shop>> connections = new HashMap<>();
        for (Integer i = 1; i <= n ; i++) {
            connections.put(i, new ArrayList<Shop>());
        }
        for(int i = 0; i < m; i++) {
            String[] roadsLine = scanner.nextLine().split("\\s+");
            int startShopId = Integer.parseInt(roadsLine[0]);
            int endShopId = Integer.parseInt(roadsLine[1]);
            int time = Integer.parseInt(roadsLine[2]);

            connections.get(startShopId).add(new Shop(endShopId, time));
            connections.get(endShopId).add(new Shop(startShopId, time));
        }

        int numOfFishTypeCombinations = 1 << k;
        // optimalTime denotes the optimal time for every node with any fish type combination
        int[][] optimalTime = new int[n + 1][numOfFishTypeCombinations];
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j < numOfFishTypeCombinations; j++) {
                optimalTime[i][j] = Integer.MAX_VALUE;    
            }
        }
        optimalTime[1][fishTypesAtShop[1]] = 0;

        Queue<ShopAssortment> queue = new LinkedList<>();
        queue.add(new ShopAssortment(1, fishTypesAtShop[1]));
        while(!queue.isEmpty()) {
            ShopAssortment shopAssortment = queue.remove();
            int time = optimalTime[shopAssortment.shopId][shopAssortment.assortment];
            int shopId = shopAssortment.shopId;
            int assortment = shopAssortment.assortment;

            for (Shop nextShop : connections.get(shopId)) {
                int nextShopId = nextShop.shopId;
                int expectedTime = time + nextShop.time;
                int nextAssortment = assortment | fishTypesAtShop[nextShopId];

                // IMPORTANT: traverse nextShopId shop only if it's optimal from time prospective
                // It can happen in 2 cases - either it's the first time visit or we're trying to get there another time from a different shop
                if (expectedTime < optimalTime[nextShopId][nextAssortment]) {
                    optimalTime[nextShopId][nextAssortment] = expectedTime;
                    queue.add(new ShopAssortment(nextShopId, nextAssortment));
                }
            }
        }

        int allFishTypes = (1 << k) - 1;
        int minTime = optimalTime[n][allFishTypes];
        // traverse each pair of configurations and pick the pair resulting into the full set of fish types and minimal optimal time
        for (int i = 1; i <= allFishTypes; i++) {
            for (int j = i; j <= allFishTypes; j++) {
                if ((i | j) == allFishTypes) {
                    minTime = Math.min(minTime, Math.max(optimalTime[n][i], optimalTime[n][j]));
                }    
            }
        }

        System.out.println(minTime);
    }
}

该解决方案通过了 Hackerrank 上的所有测试。