【问题标题】:Why does my C++ solution to the "Fish" challange by codility fail?为什么我的 C++ 解决方案对 codility 的“鱼”挑战失败了?
【发布时间】:2020-11-20 21:06:39
【问题描述】:

我正在尝试 Codility 鱼挑战,描述如下:

给定两个由 N 个整数组成的非空数组 A 和 B。 数组 A 和 B 代表河流中的 N 条贪婪的鱼,有序 顺着河流顺流而下。

鱼从 0 到 N - 1 编号。如果 P 和 Q 是两条鱼并且 P

鱼数P用A[P]和B[P]表示。数组 A 包含 鱼的大小。它的所有元素都是独一无二的。数组 B 包含 鱼的方向。它仅包含 0 和/或 1,其中:

0代表一条鱼在上游,1代表一条鱼在上游 下游。如果两条鱼朝相反的方向移动并且没有 他们之间的其他(活)鱼,他们最终会相遇 其他。那么只有一条鱼可以活着——大鱼吃掉了 较小的一个。更准确地说,我们说两条鱼 P 和 Q 相遇 其他当 P

如果 A[P] > A[Q] 那么 P 吃掉 Q,P 仍然会顺流而下, 如果 A[Q] > A[P] 则 Q 吃掉 P,Q 仍将向上游流动。我们 假设所有的鱼都以相同的速度流动。也就是鱼 朝着同一个方向前进,永远不会相遇。目标是计算 能存活的鱼的数量。

例如,考虑数组 A 和 B,这样:

A[0] = 4 B[0] = 0 A1 = 3 B1 = 1 A2 = 2 B2 = 0
A[3] = 1 B[3] = 0 A[4] = 5 B[4] = 0 最初所有的鱼都是 还活着,除了 1 号鱼外,所有的鱼都在逆流而上。鱼数 1 遇到 2 号鱼并吃掉它,然后它遇到 3 号鱼并吃掉 它也。最后,它遇到了 4 号鱼并被它吃掉了。这 剩下的两条鱼,0号和4号,永远不会相遇,因此留下 活着。

写一个函数:

int 解(向量 &A, 向量 &B);

给定两个由 N 个整数组成的非空数组 A 和 B, 返回存活的鱼的数量。

例如,给定上面显示的数组,函数应该返回 2,如上所述。

为以下假设编写一个有效的算法:

N 是 [1..100,000] 范围内的整数;数组 A 的每个元素 是 [0..1,000,000,000] 范围内的整数;的每个元素 数组 B 是一个整数,可以具有以下值之一:0、1; A 的元素都是不同的。

我的解决方法如下:

// you can use includes, for example:
// #include <algorithm>
#include <queue>
// you can write to stdout for debugging purposes, e.g.
// cout << "this is a debug message" << endl;

int solution(vector<int> &A, vector<int> &B) {
    // write your code in C++14 (g++ 6.2.0)
    std::queue<int> downStreamers;
    int deadFish = 0;

    for(int i = 0; i < (int)B.size(); i++)
    {
        int direction = B[i];
        int size = A[i];

        if(direction == 1)
            downStreamers.push(size);
        else
        {
            while(!downStreamers.empty()) 
            {
                deadFish += 1;
                if(size > downStreamers.front())
                {  
                    downStreamers.pop();
                }
                else
                    break;
            }
        }
    }

    return A.size()-deadFish;

}

这是我设计这段代码的方式(在我的脑海中):

称为 downStreamers 的队列结构将包含向下游方向移动的鱼 (1)

deadFish 将包含因方向碰撞而发生的死亡计数器

循环:

对于每条鱼我

  • 检查 i 鱼是否在下游 (1),如果是,则添加所述的大小 排在下游队列后面的鱼。
  • 鉴于第 i 条鱼正在上游 (0),我想增加 deadFish 计数器,因为队列不为空。如果鱼被 downStreamers 堆栈击败,只需退出循环,否则从 downStreamers 中弹出鱼,直到它为空。

在循环结束时,结果应该是鱼的数量 (A.size()) - deadFishes。这适用于简单的测试用例。但是,它无法解决隐藏的问题,并且 codility 并没有提供太多关于哪里出了问题的反馈。

这是我的测试结果。

如果有人能向我提供有关如何以万无一失的方式解决这些问题的见解,我将不胜感激。

【问题讨论】:

  • 你能在问题中添加挑战描述吗?
  • 我更新了帖子。

标签: c++


【解决方案1】:

我怀疑您的代码存在使用队列的问题。我认为它会起作用,如果你用堆栈替换它。考虑这个例子:

vector<int> A = { 4,2,1,3 };
vector<int> B = { 1,0,1,0 };

一叠:0吃1,3吃2,然后0吃3。

使用您的队列:0 吃 1,0 吃 3,2 不能再吃。

注意:队列是先进先出的。另一方面,堆栈是后进先出的。这就是你想要的。

【讨论】:

    【解决方案2】:

    我将为您发布一些算法思想和想法。希望这可以帮助您了解问题。

    一些初步观察 - 所有在阵列开始上游游动的鱼永远不会吃或被吃掉。阵列底部的鱼向下游游动也是如此。当下游遇到上游时从前向后扫描时发生该动作。

    首先,从前到后移动你的阵列,直到找到一条向下游泳的鱼。

    接下来继续向下/向上移动阵列,直到它从下到上交换。

    解决战斗并增加战斗计数器。

    如果向下获胜,请继续向下走。如果向上获胜,则将您所在位置的索引保存在数组中,然后沿着数组向上走,直到它被吃掉或者你碰到一条向同一方向游动的鱼。

    如果它发现一条鱼在它的方向游动,它就会活下来,然后跳回到它开始向下扫描的地方。

    如果它被吃掉了,把这条鱼拿走,然后开始比较你在 down 数组中离开的地方。

    重复直到到达列表的末尾。返回(起始鱼数-战斗次数)

    整洁的部分是这是就地的。无需移动内存,无需标记死鱼等。它将运行线性时间 - 最多接近 2 倍线性。

    这是我用来解决这个问题的伪代码。

    【讨论】:

    • 嗨,这是解决这个问题的一种很好的直观方法,但是当我尝试实现它时,很难跟踪嵌套循环和 if 语句。
    【解决方案3】:

    这是一个(诚然适度复杂的)解决方案:

    // you can use includes, for example:
    // #include <algorithm>
    #include <list>
    #include <stack>
    
    // you can write to stdout for debugging purposes, e.g.
    // cout << "this is a debug message" << endl;
    
    int solution(vector<int> &A, vector<int> &B) {
        list<int> fish;
        stack<int> upstream;
        for (int ii = A.size() - 1; ii >= 0; --ii) {
            if (B[ii] == 0)
                upstream.push(A[ii]);
            else {
                fish.push_back(A[ii]);
                while ( !upstream.empty() ) {
                    if ( upstream.top() > fish.back() ) {
                        fish.pop_back();
                        fish.push_back( upstream.top() );
                        upstream.pop();
                        --ii;
                        if (ii < 0) {
                            ++ii;
                            break;
                        } else {
                            while (!fish.empty() && ii < (int) B.size()) {
                                if (B[ii] == 0) {
                                    upstream.push(fish.back());
                                    fish.pop_back();
                                    ++ii;
                                    break;
                                }
                                if ( fish.back() > A[ii] )
                                    --ii;
                                else {
                                    ++ii;
                                    fish.pop_back();
                                    break;
                                }
                            }
                            break;
                        }
                    } else
                        upstream.pop();
                }
            }
        }
    
        int add_size = 0;
        if ( !upstream.empty() )
            add_size = upstream.size();
    
        return fish.size() + add_size;
    }
    

    算法:

    • 鱼大小的输入向量A 被遍历。
    • 上述解决方案将上游游泳的鱼分配到一个堆栈中。
    • 当鱼 ii 向下游游动 (B[ii] = 0) 时,它会与 upstream 堆栈中的鱼进行比较。
    • 如果堆栈中的鱼在比较中获胜,它将替换列表解决方案中的鱼ii(我将其命名为fish),但它不会从upstream 堆栈中弹出。
    • 只有当堆栈中的鱼被击败时,它才会从堆栈中弹出。
    • 遍历输入后,返回列表和堆栈中幸存者的总和。

    这通过了@xeed 提出的测试,并在 Codility 上获得了 100% 的总体评分。 对于一个完整的 MWE,这里有一个驱动主测试程序:

    int main()
    {
        vector<int> A = {4,3,2,1,5};
        vector<int> B = {0,1,0,0,0};
        int sol = solution(A, B);
        cout << "A = {4,3,2,1,5}, B = {0,1,0,0,0}? 2: " << sol << endl;
    
        A = {1,0};
        B = {1,0};
        sol = solution(A, B);
        cout << "A = {1,0}, B = {1,0}? 1: " << sol << endl;
    
        A = {0,1};
        B = {0,0};
        sol = solution(A, B);
        cout << "A = {0,1}, B = {0,0}? 2: " << sol << endl;
    
        A = {0};
        B = {1};
        sol = solution(A, B);
        cout << "A = {0}, B = {1}? 1: " << sol << endl;
    
        A = {1};
        B = {0};
        sol = solution(A, B);
        cout << "A = {1}, B = {0}? 1: " << sol << endl;
    
        A = {7,4,3,2,5,6};
        B = {0,1,1,1,0,1};
        sol = solution(A, B);
        cout << "A = {7,4,3,2,5,6}, B = {0,1,1,1,0,1}? 3: " << sol << endl;
    
        A = {3,4,2,1,5};
        B = {1,0,0,0,0};
        sol = solution(A, B);
        cout << "A = {3,4,2,1,5}, B = {1,0,0,0,0}? 4: " << sol << endl;
    
        A = {1,2};
        B = {0,1};
        sol = solution(A, B);
        cout << "A = {1,2}, B = {0,1}? 2: " << sol << endl;
    
        A = {4,2,1,3};
        B = {1,0,1,0};
        sol = solution(A, B);
        cout << "A = {4,2,1,3}, B = {1,0,1,0}? 1: " << sol << endl;
    
        A = {4,3,2,5,6};
        B = {1,0,1,0,1};
        sol = solution(A, B);
        cout << "A = {4,3,2,5,6}, B = {1,0,1,0,1}? 2: " << sol << endl;
    
        return 0;
    }
    

    【讨论】:

      【解决方案4】:

      这是我为您的测试用例编写的解决方案:

      #include <stdio.h>
      #include <vector>
      #include <iostream>
      
      int main()
      {
          std::vector<int> fishSize = { 4, 3, 2, 1, 5 };
          std::vector<int> fishDirection = { 0, 1, 0, 0, 0 };
          
          int numberOfFish = fishSize.size();
          for (int i=0; i < numberOfFish; i++) {
              int curFishSize = fishSize[i];
              int curFishDirection = fishDirection[i];
              if (curFishDirection == 0) {
                  // Direction down
                  for (int j = i - 1; j > 0; j--) {
                      int compareFishSize = fishSize[j];
                      int compareFishDirection = fishDirection[j];
                      if (compareFishDirection == 1 && compareFishSize < curFishSize) {
                          // Eat this fish
                          fishSize[j] = -1;
                          fishDirection[j] = -1;
                          std::cout << "Direction Down: Eating Fish " << j << " by " << i << std::endl;
                      } else if (compareFishDirection == -1) {
                          continue;
                          
                      } else {
                          break;
                      }
                  }
                  
              } else if (curFishDirection == 1) {
                  // Direction up
                  for (int j = i+1; j < numberOfFish; j++) {
                      int compareFishSize = fishSize[j];
                      int compareFishDirection = fishDirection[j];
                      if (compareFishDirection == 0 && compareFishSize < curFishSize) {
                          // Eat this fish
                          std::cout << "Direction Up: Eating Fish " << j << " by " << i << std::endl;
                          fishSize[j] = -1;
                          fishDirection[j] = -1;
                      } else if (compareFishDirection == -1) {
                          continue;
                      } else {
                          break;
                      }
                  }
              }
          }
          
          int numberOfFishRemaining = 0;
          for(int a : fishSize) {
              if (a != -1) numberOfFishRemaining++;
          }
          
          std::cout << "Number Of Fish Remaining are: " << numberOfFishRemaining << std::endl;
      
      
          return 0;
      }
      

      此解决方案将生成以下输出:

      Direction Up: Eating Fish 2 by 1                                                                                                                     
      Direction Up: Eating Fish 3 by 1                                                                                                                     
      Direction Down: Eating Fish 1 by 4                                                                                                                   
      Number Of Fish Remaining are: 2 
      

      我希望这也能解决你所有的测试用例。

      【讨论】:

        猜你喜欢
        • 2017-10-03
        • 1970-01-01
        • 2013-12-28
        • 1970-01-01
        • 2014-12-21
        • 1970-01-01
        • 1970-01-01
        • 2019-02-19
        • 2020-01-16
        相关资源
        最近更新 更多