【问题标题】:Correct Algorithm for Game of two stacks on HackerRankHackerRank 上两堆游戏的正确算法
【发布时间】:2017-10-20 09:25:58
【问题描述】:

我刚刚在 HackerRank 上尝试了一个基于堆栈的问题

https://www.hackerrank.com/challenges/game-of-two-stacks

Alexa 有两个非负整数堆栈,堆栈 A 和堆栈 B,其中索引 0 表示堆栈的顶部。 Alexa 挑战尼克玩以下游戏:

在每一步中,尼克可以从堆栈 A 或 B 堆栈的顶部移除一个整数。

Nick 保留他从两个堆栈中删除的整数的运行总和。

如果在任何时候,尼克的运行总和大于游戏开始时给出的某个整数 X,则尼克将被取消比赛资格。

Nick 的最终分数是他从两个堆栈中删除的整数总数。

找出尼克在每场比赛中可以达到的最大可能分数(即他可以在不被取消资格的情况下删除的最大整数数)并将其打印在新行上。

对于每场比赛,在新行上打印一个整数,表示尼克在不被取消资格的情况下可以达到的最高分。

Sample Input 0

1 -> Number of games
10 -> sum should not exceed 10 
4 2 4 6 1  -> Stack A
2 1 8 5 -> Stack B

Sample Output 

4

下面是我的代码,我尝试了贪婪的方法,从堆栈顶部获取最小元素并将其添加到总和中。它适用于某些测试用例,但在下面的输入中失败了

1
67
19 9 8 13 1 7 18 0 19 19 10 5 15 19 0 0 16 12 5 10 - Stack A
11 17 1 18 14 12 9 18 14 3 4 13 4 12 6 5 12 16 5 11 16 8 16 3 7 8 3 3 0 1 13 4 10 7 14 - Stack B

我的代码是5,但正确的解决方案是6,串联弹出的元素是19,9,8,11,17,1 堆栈 A 中的前三个元素和堆栈 B 中的前三个元素。

**

我不明白算法在我看来,任何人都可以使用 DP 帮我解决方法/算法?

**

public class Default {

    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        int numOfGames = Integer.parseInt(br.readLine());
        for (int i = 0; i < numOfGames; i++) {
            String[] tmp = br.readLine().split(" ");
            int numOfElementsStackOne = Integer.parseInt(tmp[0]);
            int numOfElementsStackTwo = Integer.parseInt(tmp[1]);
            int limit = Integer.parseInt(tmp[2]);
            int sum = 0;
            int popCount = 0;

            Stack<Integer> stackOne = new Stack<Integer>();
            Stack<Integer> stackTwo = new Stack<Integer>();

            String[] stOne = br.readLine().split(" ");
            String[] stTwo = br.readLine().split(" ");

            for (int k = numOfElementsStackOne - 1; k >= 0; k--) {
                stackOne.push(Integer.parseInt(stOne[k]));
            }

            for (int j = numOfElementsStackTwo - 1; j >= 0; j--) {
                stackTwo.push(Integer.parseInt(stTwo[j]));
            }

            while (sum <= limit) {
                int pk1 = 0;
                int pk2 = 0;
                if (stackOne.isEmpty()) {
                    sum = sum + stackTwo.pop();
                    popCount++;
                } else if (stackTwo.isEmpty()) {
                    sum = sum + stackOne.pop();
                    popCount++;
                } 
                else if (!stackOne.isEmpty() && !stackTwo.isEmpty()) {
                    pk1 = stackOne.peek();
                    pk2 = stackTwo.peek();

                    if (pk1 <= pk2) {
                        sum = sum + stackOne.pop();
                        popCount++;
                    } else {
                        sum = sum + stackTwo.pop();
                        popCount++;
                    }
                } else if(stackOne.isEmpty() && stackTwo.isEmpty()){
                    break;
                }
            }

            int score = (popCount>0)?(popCount-1):0;
            System.out.println(score);
        }
    }
}

【问题讨论】:

  • 当您收到的输出不正确时,我不确定您是否可以声称已解决问题。让别人把你的尝试变成正确的解决方案,这不是有点像作弊吗? :-) 但是您清楚地描述了问题、解决方案尝试和意外结果,因此您得到了我的投票。
  • 我真的不认为这是作弊。这就是学习在你不理解的概念上借助社区的帮助。除了我不是参加比赛,这只是一个练习题
  • 你注意到我赞成你的意见吗?

标签: java algorithm data-structures stack


【解决方案1】:

好的,我将尝试解释一个基本上可以用 O(n) 解决这个问题的算法,您需要自己尝试编码。

我会用简单的例子来解释,你可以反映一下

1 -> Number of games
10 -> sum should not exceed 10  
4 2 4 6 1  -> Stack A
2 1 8 5 -> Stack B

首先,您需要创建 2 个数组,该数组将包含直到其堆栈索引的所有数字的总和,例如对于堆栈 A,您将拥有此数组

4 6 10 16 17  //index 0 ->4

堆栈 B 也会这样做

2 3 11 16

然后对于每个数组,从数组末尾开始迭代,直到达到小于或等于“您不应超过的总和”的数字

现在您当前的总和是您在两个数组中达到的点的总和,应该是 10 +3 = 13 所以为了达到 10 绝对需要删除更多条目

要删除额外的条目,我们将再次移动数组上的索引,以决定要移动哪个数组的索引,获取您指向的条目(数组 1 为 10,数组 2 为 3)并按索引进行设备+1 (10/3 ~ 3) , (3/2 ~1) 然后将索引移动到最大值并重新计算总和

假设我们有:

a = 1 1 1 211 2
b = 1 85

和 maxSum = 217 现在,在计算前缀和时,

a' = 1 2 3 214 216
and b' = 1 86
current sum = 86+216 > 217

所以要决定删除哪个索引,我们比较`

216/5~43.2` and `86/2=43`, 

所以我们将指针移入a'。但是,这并没有解决它 - `

214+86 is still > 217!!` 

如果我们删除了 86,那就更好了!所以我们应该总是删除与前一个元素有较大差异的那个!

如果两个值相等,则将索引移动到与其前一个差异较大的值上是逻辑的(记住我们正在以相反的顺序移动索引)。​​

结果将是索引的总和 +2。

【讨论】:

  • 我有点理解您的解决方案 Amer,但这不是一个显而易见的解决方案,这是解决这类问题的某种算法/特定的数学方法吗?我最初想到了解决这个 A 的两种方法。取 Stack A 和 Stack B 的总和并找到最大元素。 B. 贪心方法每次都在顶部找到最小元素。但是你建议的那个不是一个明显的。
  • @underdog 有很多方法可以贪婪地解决它,但无论如何你需要访问两个堆栈,我的解决方案旨在反向访问堆栈,因为这将使我摆脱拥抱的可能性如果我相反,取最大元素有一个问题,你不知道包含元素的数量,我同意解决方案并不明显,我只是想出了这个算法,也许稍后我会编码并分享它
【解决方案2】:

python3 中的解决方案

# stack implementation
class Stack:
    lis = []

    def __init__(self, l):
        self.lis = l[::-1]

    def push(self, data):
        self.lis.append(data)

    def peek(self):
        return self.lis[-1]

    def pop(self):
        self.lis.pop()

    def is_empty(self):
        return len(self.lis) == 0


# number of test cases
tests = int(input())
for i in range(tests):
    na, nb, x = map(int, input().split(' '))
    a = list(map(int, input().split(' ')))
    b = list(map(int, input().split(' ')))
    temp = []
    stk_a = Stack(a)
    stk_b = Stack(b)
    score = 0
    count = 0
# first taking elements from stack A , till score becomes just less than desired total
    for j in range(len(a)):
        if score + stk_a.peek() <= x:
            score += stk_a.peek()

            count += 1
            temp.append(stk_a.peek())
            # storing the popped elements in temporary stack such that we can again remove them from score
            # when we find better element in stack B
            stk_a.pop()
# this is maximum number of moves using only stack A
    max_now = count
# now iterating through stack B for element lets say k which on adding to total score should be less than desired
    # or else we will remove each element of stack A from score till it becomes just less than desired total.
    for k in range(len(b)):
        score += stk_b.peek()
        stk_b.pop()
        count += 1
        while score > x and count > 0 and len(temp) > 0:
            count = count - 1
            score = score - temp[-1]
            temp.pop()
        # if the score after adding element from stack B is greater than max_now then we have new set of moves which will also lead
        # to just less than desired so we should pick maximum of both
        if score <= x and count > max_now:
            max_now = count
    print(max_now)

【讨论】:

    【解决方案3】:

    这个解决方案效果很好......我希望它有帮助......

       import java.util.Scanner;
    
    public class Solution {
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
    
            int g = sc.nextInt();
            for (int tc = 0; tc < g; tc++) {
                int n = sc.nextInt();
                int m = sc.nextInt();
                int x = sc.nextInt();
                int[] a = readArray(sc, n);
                int[] b = readArray(sc, m);
    
                System.out.println(solve(a, b, x));
            }
    
            sc.close();
        }
    
        static int[] readArray(Scanner sc, int size) {
            int[] result = new int[size];
            for (int i = 0; i < result.length; i++) {
                result[i] = sc.nextInt();
            }
            return result;
        }
    
        static int solve(int[] a, int[] b, int x) {
            int lengthB = 0;
            int sum = 0;
            while (lengthB < b.length && sum + b[lengthB] <= x) {
                sum += b[lengthB];
                lengthB++;
            }
    
            int maxScore = lengthB;
            for (int lengthA = 1; lengthA <= a.length; lengthA++) {
                sum += a[lengthA - 1];
    
                while (sum > x && lengthB > 0) {
                    lengthB--;
                    sum -= b[lengthB];
                }
    
                if (sum > x) {
                    break;
                }
    
                maxScore = Math.max(maxScore, lengthA + lengthB);
            }
            return maxScore;
        }
    }
    

    【讨论】:

    • 你能解释一下算法吗?
    • 你能解释一下算法吗?
    • 请解释一下算法。
    【解决方案4】:
    void traversal(int &max, int x, std::vector<int> &a, int pos_a,
               std::vector<int> &b, int pos_b) {
    
      if (pos_a < a.size() and a[pos_a] <= x) {
        max = std::max(pos_a + pos_b + 1, max);
        traversal(max, x - a[pos_a], a, pos_a + 1, b, pos_b);
      }
    
      if (pos_b < b.size() and b[pos_b] <= x) {
        max = std::max(pos_a + pos_b + 1, max);
        traversal(max, x - b[pos_b], a, pos_a, b, pos_b + 1);
      }
    }
    
    int twoStacks(int x, std::vector<int> &a, std::vector<int> &b) {
      int max = 0;
      traversal(max, x, a, 0, b, 0);
      return max;
    }
    

    递归解法,简单易懂。该解决方案将 2 个堆栈作为有向图并对其进行遍历。

    【讨论】:

      【解决方案5】:

      我看到存在解决方案,您将其标记为正确,但我有一个简单的解决方案

      1. 从栈一中添加所有满足条件的元素
      2. 您添加的每个元素都将其推送到名为 elements_from_a 的堆栈中
      3. 将计数器设置为堆栈大小
      4. 如果 sum > x 则尝试从堆栈 b 添加元素,因此删除您添加的最后一个元素,您可以从堆栈 elements_from_a 中获取它
      5. 每次添加时增加 bstack 计数器,每次删除时从堆栈中减少
      6. 将步数总和与计数进行比较并调整计数返回计数

      这是解决方案的代码示例:

      def twoStacks(x, a, b):
      sumx = 0
      asteps = 0
      bsteps = 0
      elements = []
      maxIndex = 0
      
      while len(a) > 0 and sumx + a[0] <= x :
          nextvalue =  a.pop(0)
          sumx+=nextvalue
          asteps+=1
          elements.append(nextvalue)
      
      maxIndex = asteps
      
      
      while len(b) > 0 and len(elements) > 0:
          sumx += b.pop(0)
          bsteps+=1
          while sumx > x and len(elements) > 0 :
              lastValue = elements.pop()
              asteps-=1
              sumx -= lastValue
      
      
      
          if sumx <= x and bsteps + asteps > maxIndex :
              maxIndex = bsteps + asteps
      
      
      
      return maxIndex
      

      我希望这是更简单的解决方案。

      【讨论】:

        【解决方案6】:

        接受的答案是错误的。如图所示,以下测试用例失败。

        对于给定的测试用例,如果最大和不超过10,那么正确答案是5。但是如果我们遵循Amer Qarabsa的方法,答案将是3。我们可以遵循Geeky coder的方法。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多