【问题标题】:Algorithm for rock, paper, scissors game石头、纸、剪刀游戏的算法
【发布时间】:2020-10-26 21:32:35
【问题描述】:

所以这是对古老的石头、纸、剪刀 java 示例的一个稍微不同的看法。在这种情况下,用户输入输入(我假设它是有效的,即仅使用 R、P 和 S 的组合,并且有匹配的括号,也没有空格),例如 (R&S) 输出是 @ 987654322@ 因为 Rock 胜过 Scissors,或者 ((R&S)&(P&R)) 输出 P

到目前为止,我的代码(如下)可以拆分字符串,遍历并将使用的字母放入列表中,因为我的想法只是从左到右阅读,评估直到我读到最后,但在这一点我很困惑,因为什么是跟踪所有“先前”结果的好方法。我需要另一个空列表吗?此外,使用案例似乎不可行,因为输入可能很长,而且 R、P 和 S 的完全随机组合。任何建议都值得赞赏!

import java.util.ArrayList;
import java.util.Scanner;

public class RPS {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String str = sc.nextLine();
        str = str.replaceAll("\\(", "").replaceAll("\\)","");
        String inputs[] = str.split("&");
        ArrayList<Object> list = new ArrayList<>();

        for (int i = 0; i < inputs.length; i++){
            if (inputs[i].substring(0, 1).contains("R")) {
                list.add(inputs[i]);
            } else if (inputs[i].substring(0, 1).contains("S")) {
                list.add(inputs[i]);
            } else if (inputs[i].substring(0, 1).contains("P")) {
                list.add(inputs[i]);
            }
        }
        for (int i = 0; i < list.size(); i++){
            if (list.contains("R") && list.contains("S")){ //ex. if the input was "(R&S)"
                System.out.println("R");
                break;
            }
        }

    }

}

【问题讨论】:

  • 它的潜在复杂性和嵌套很大,我想知道您是否想要使用正式的词法分析器/解析器,例如 ANTLR4。
  • 请考虑使用堆栈结构来解决这个问题。因此,您可以随时评估简单的操作并将结果放回堆栈以供进一步使用。
  • R&amp;R 会给什么?
  • @btilly 哦,对了,那是 R,与 S&S、P&P 相同。 R,P,S的单个输入也只是它们自己

标签: java algorithm


【解决方案1】:

解决此问题的一种方法是编写递归evaluate 函数。它将一个字符串作为输入,基本情况是单个字符RPS。否则,它将在顶级 & 符号上拆分字符串,并递归计算 & 符号左右的字符串,并使用返回的字符来确定结果。顶级 & 符号可以作为出现在 not 括号内的 & 符号找到(不计算任何最外面的冗余括号,如果它们存在的话)。

例如,这里有一个 Java 实现。

import java.util.Stack;

public class RPS {
    // Normalize string by removing surrounding parentheses that are redundant.
    private static String normalize(String s) {
        // First, count the number of leading open parentheses.
        int leading = 0;
        for (int i = 0; i < s.length(); ++i) {
            if (s.charAt(i) != '(')
                break;
            ++leading;
        }
        if (leading > 0) {
            // For each closing parenthesis, compute the position of the
            // matching opening parenthesis. The set of trailing parentheses
            // paired with leading parentheses are redundant.
            Stack<Integer> stack = new Stack<Integer>();
            for (int i = 0; i < s.length(); ++i) {
                char c = s.charAt(i);
                if (c == '(') {
                    stack.push(i);
                } else if (c == ')') {
                    int j = stack.pop();
                    if (j < leading && j + i + 1 == s.length())
                        return s.substring(j + 1, s.length() - j - 1);
                }
            }   
        }
        return s;
    }
    
    private static char evaluate(String s) {
        s = normalize(s);
        // A single character evaluates to itself
        if (s.length() == 1)
            return s.charAt(0);
        // Find the position of the top-level ampersand, which is the ampersand
        // occurring outside matched pairs of parentheses.
        int depth = 0;
        int position = -1;
        for (int i = 0; i < s.length(); ++i) {
            char c = s.charAt(i);
            if (c == '(')
                depth += 1;
            else if (c == ')')
                depth -= 1;
            else if (depth == 0 && c == '&') {
                position = i;
                break;
            }
        }
        // Otherwise, split on the top-level ampersand and evaluate the left
        // and right sides.
        char left = evaluate(s.substring(0, position));
        char right = evaluate(s.substring(position + 1));
        // Return the winner
        if (left == right)
            return left;
        switch (left) {
            case 'R':
                return right == 'P' ? 'P' : 'R';
            case 'P':
                return right == 'S' ? 'S' : 'P';
            case 'S':
                return right == 'R' ? 'R' : 'S';
            default:
                throw new RuntimeException();    
        }
    }
    
    public static void main(String[] args) {
        System.out.println(evaluate("((R&S)&(P&R))"));
        System.out.println(evaluate("(R&S)&(P&R)"));
        System.out.println(evaluate("(R)"));
        System.out.println(evaluate("((((R&P))&((S))))"));
        System.out.println(evaluate("((R&S)&R)"));
        System.out.println(evaluate("S"));
        System.out.println(evaluate("(((R&P)&(S&P))&(P&R))"));
    }
}

输出:

P
P
R
S
R
S
S

【讨论】:

  • 对于我的原始答案,我假设输入没有多余的括号,并且最外面的表达式被括号包围。我已经更新了上面的解决方案以放宽这些假设。例如,(R&amp;S)&amp;(P&amp;R)(((R&amp;S))) 不适用于我原来的解决方案,但它们现在可以使用。
【解决方案2】:

如果您处理输入并将其转换为reverse polish notation,则可以使用Stack 来保存值和运算符。

这就是我的意思。接受您的简单输入。

(R&S)

Stack 上,它看起来像:

R
S
&

堆栈应始终以两个值和一个运算符开始。

让我们举个更复杂的例子。

((R&S)&(P&R))

Stack 上,它看起来像:

 R
 S
 &
 P
 R
 &
 &

您可以将Stack 上的RS&amp; 替换为结果。然后,您将Stack 上的PR&amp; 替换为结果。最终结果将使用最后一个 &amp; 运算符进行处理。

【讨论】:

  • 用 RPN 替换它的算法应该和原始问题一样难。
  • 是的。但是将“中缀”转换为 RPN 是一个众所周知的问题,有众所周知的解决方案。
  • 使用二元运算符解析中缀表示法也是如此。例如,请参阅blog.erezsh.com/…。如果您没有解释您知道的算法而 OP 没有解释,那么您的建议对 OP 没有多大帮助。
【解决方案3】:

解决此问题的一种方法是首先将字符串解析为树,其中节点是 1) 一个字符或 2) 一组表示括号中项目的子节点。然后在树上调用递归求值函数。

例如,这是一个 Java 实现,它具有错误检查和对空格(被忽略)的支持,然后是我在 Python 中的初始原型,它更短,但不包括错误检查也不支持空格。

Java

import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Set;

public class RPS {
    private static class Node {
        Character data = null;
        List<Node> children = null;
    }
    
    private static Node parse(Queue<Character> tokens) {
        // WARN: Destructively modifies input
        char token = tokens.remove();
        Node node = new Node();
        if (token == '(') {
            node.children = new ArrayList<Node>();
            while (tokens.peek() != ')') {
                node.children.add(parse(tokens));
            }
            char c = tokens.remove();
            if (c != ')')
                throw new RuntimeException();
        } else if (token == ')') {
            throw new RuntimeException();
        } else {
            node.data = token;
        }
        return node;
    }
    
    private static char _evaluate(Node tree) {
        if (tree.data != null) {
            return tree.data;
        } else if (tree.children.size() == 1) {
            return _evaluate(tree.children.get(0));
        } else {
            char left = _evaluate(tree.children.get(0));
            if (!tree.children.get(1).data.equals('&'))
                throw new RuntimeException();
            char right = _evaluate(tree.children.get(2));
            // Return the winner
            if (left == right)
                return left;
            switch (left) {
                case 'R':
                    return right == 'P' ? 'P' : 'R';
                case 'P':
                    return right == 'S' ? 'S' : 'P';
                case 'S':
                    return right == 'R' ? 'R' : 'S';
                default:
                    throw new RuntimeException();    
            }
        }
    }
    
    private static Set<Character> VALID_CHARS =
            new HashSet<Character>() {{
                add('(');
                add(')');
                add('&');
                add('R');
                add('P');
                add('S');
            }};
    
    public static char evaluate(String s) {
        Queue<Character> tokens = new LinkedList<Character>();
        tokens.add('(');
        for (int i = 0; i < s.length(); ++i) {
            char c = Character.toUpperCase(s.charAt(i));
            if (Character.isWhitespace(c))
                continue;
            if (!VALID_CHARS.contains(c))
                throw new RuntimeException();
            tokens.add(c);
        }
        tokens.add(')');
        Node tree = parse(tokens);
        char c = _evaluate(tree);
        return c;
    }
    
    public static void main(String[] args) {
        System.out.println(evaluate("((R&S)&(P&R))"));          // P
        System.out.println(evaluate("(R&S)&(P&R)"));            // P
        System.out.println(evaluate("( R )"));                  // R
        System.out.println(evaluate("((((R&P))&((S))))"));      // S
        System.out.println(evaluate("((R&S)&R)"));              // R
        System.out.println(evaluate("S"));                      // S
        System.out.println(evaluate("(((R&P)&(S&P))&(P&R))"));  // S
    }
}

Python

from collections import deque
from typing import Union

def parse(tokens: deque):
    # WARN: Destructively modifies input
    token = tokens.popleft()
    if token == '(':
        result = []
        while tokens[0] != ')':
            result.append(parse(tokens))
        tokens.popleft()  # closing ')'
        return result
    else:
        return token

def _evaluate(tree: Union[list, str]):
    if type(tree) != list:
        return tree
    elif len(tree) == 1:
        return _evaluate(tree[0])
    else:
        left = _evaluate(tree[0])
        right = _evaluate(tree[2])
        pair = left + right
        lookup = {
            'RP': 'P', 'PR': 'P', 'PP': 'P',
            'RS': 'R', 'SR': 'R', 'RR': 'R',
            'PS': 'S', 'SP': 'S', 'SS': 'S',
        }
        return lookup[pair]

def evaluate(s: str):
    tokens = deque(f'({s})')
    tree = parse(tokens)
    return _evaluate(tree)

# Example usage
print(evaluate('((R&S)&(P&R))'))          # P
print(evaluate('(R&S)&(P&R)'))            # P
print(evaluate('(R)'))                    # R
print(evaluate('((((R&P))&((S))))'))      # S
print(evaluate('((R&S)&R)'))              # R
print(evaluate('S'))                      # S
print(evaluate('(((R&P)&(S&P))&(P&R))'))  # S

【讨论】:

    猜你喜欢
    • 2013-12-09
    • 1970-01-01
    • 2015-01-15
    • 1970-01-01
    • 1970-01-01
    • 2017-01-17
    • 2015-05-16
    • 2020-05-22
    • 1970-01-01
    相关资源
    最近更新 更多