CF 102386B - Турнир УрФУ
We need to judge one round of Rock-Paper-Scissors-Lizard-Spock. The first input line is the move chosen by the first player, and the second line is the move chosen by the second player. Each move is one of Rock, Scissors, Paper, Lizard, or Spock.
CF 102386B - \u0422\u0443\u0440\u043d\u0438\u0440 \u0423\u0440\u0424\u0423
Rating: -
Tags: -
Solve time: 6m 18s
Verified: no
Solution
Problem Understanding
We need to judge one round of Rock-Paper-Scissors-Lizard-Spock. The first input line is the move chosen by the first player, and the second line is the move chosen by the second player. Each move is one of Rock, Scissors, Paper, Lizard, or Spock.
Every move defeats exactly two other moves. Scissors defeats Paper and Lizard, Paper defeats Rock and Spock, Rock defeats Lizard and Scissors, Lizard defeats Spock and Paper, and Spock defeats Scissors and Rock. If both players choose the same move, the result is a tie.
The program must print First when the first move defeats the second, Second when the reverse is true, and Tie when the moves are equal.
There is no variable-sized input here. Exactly two strings are read, and each belongs to a fixed set of five possible values. Consequently, even a method that explicitly considers every possible pair of moves performs at most 25 comparisons. There is no meaningful large-n performance issue in this problem, so an O(1) solution is sufficient and easily fits any normal Codeforces limit.
The main edge cases come from treating the game as ordinary Rock-Paper-Scissors or from forgetting that every move has two winning opponents. For example,
Rock
Rock
must produce Tie. A careless implementation that only checks whether the first move beats the second could fall through to Second instead of handling equality first.
Another case is
Lizard
Spock
which produces First. Lizard defeats Spock, even though neither move belongs to the three standard choices from ordinary Rock-Paper-Scissors. An implementation containing only the classic three relationships would give an incorrect result.
A third useful boundary case is
Spock
Paper
which produces Second, because Paper defeats Spock. Checking only one of the two winning relationships for each move would miss this case.
Approaches
A direct brute-force solution can explicitly enumerate all 25 ordered pairs of moves and associate each pair with its result. Since there are only five possible moves for each player, the worst case is exactly 25 pair checks. This approach is already fast enough, because 25 is a constant independent of the input size. There is no input size at which this particular brute-force method becomes too slow.
A more natural implementation uses the structure of the game itself. We store the ten directed winning relationships, then check whether the first player's move is one of the moves that defeats the second player's move. If so, the first player wins. Otherwise, if the moves are equal, the result is a tie. Every remaining pair must mean that the second player wins, because the rules define a winner for every pair of distinct moves.
The key observation is that the entire game is a fixed graph with only five vertices. Each move is a vertex, and an edge from A to B means that A defeats B. We do not need to search this graph or build anything dynamically. A constant-sized lookup structure directly represents all possible winning relationships.
The two approaches have the following complexity.
| Approach | Time Complexity | Space Complexity | Verdict |
|---|---|---|---|
| Enumerate all 25 possible pairs | O(1) | O(1) | Accepted |
| Winning-relation lookup | O(1) | O(1) | Accepted |
The lookup approach is preferable because it represents the rules directly and avoids a long chain of special cases.
Algorithm Walkthrough
- Read the two moves into
firstandsecond. There are exactly two input lines, so no test-case loop is needed. - If
first == second, printTie. Equal moves never defeat each other, regardless of which move they are. - Store the two moves defeated by each possible move. For example,
Rockis associated withLizardandScissors, whileSpockis associated withRockandScissors. - Check whether
secondbelongs to the set of moves defeated byfirst. If it does, printFirst. - If the moves are different and the first move does not defeat the second, print
Second. Every distinct pair has exactly one winner, so there is no fourth outcome to consider.
Why it works
For every move A, the lookup structure contains exactly the two moves that A defeats according to the game rules. After the equality check, the two players have distinct moves. If the second move appears in the winning set of the first move, the rules say that the first player wins. Otherwise, the first player cannot defeat the second, and because every distinct pair has a winner, the second player must win. Thus every possible input reaches exactly its correct result.
Python Solution
import sys
input = sys.stdin.readline
first = input().strip()
second = input().strip()
wins = {
"Rock": {"Lizard", "Scissors"},
"Scissors": {"Paper", "Lizard"},
"Paper": {"Rock", "Spock"},
"Lizard": {"Spock", "Paper"},
"Spock": {"Scissors", "Rock"},
}
if first == second:
print("Tie")
elif second in wins[first]:
print("First")
else:
print("Second")
The dictionary wins is the complete representation of the game graph. Each key is one possible first-player move, and its value contains exactly the two moves that it defeats.
The equality check comes before the winning lookup because equality has its own result, Tie. Without this check, an equal pair would incorrectly fall into the Second case.
The expression second in wins[first] checks precisely the condition needed for the first player to win. If it is false after the moves have already been shown to differ, the second player necessarily wins.
There are no indexes, loops over input data, or arithmetic operations here, so there are no boundary or integer-overflow concerns. The .strip() calls remove the newline characters produced by readline() while preserving the exact move names.
Worked Examples
Sample 1
The input is:
Rock
Paper
The relevant state changes are:
| Step | first |
second |
Condition | Result |
|---|---|---|---|---|
| Read input | Rock |
Paper |
Both moves stored | Continue |
| Equality check | Rock |
Paper |
first == second is false |
Continue |
| Winning lookup | Rock |
Paper |
Paper is not defeated by Rock |
Second |
Rock defeats Lizard and Scissors, not Paper. Since the moves differ, the only remaining winner is the second player. The program prints Second.
Sample 2
The input is:
Rock
Rock
The trace is:
| Step | first |
second |
Condition | Result |
|---|---|---|---|---|
| Read input | Rock |
Rock |
Both moves stored | Continue |
| Equality check | Rock |
Rock |
first == second is true |
Tie |
The lookup is never needed. This demonstrates why equality must be handled before checking the winning relationships.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(1) | Only two strings are read and one constant-sized lookup is performed. |
| Space | O(1) | The dictionary contains exactly five keys and ten winning relationships. |
The input size is fixed at two moves from a five-element set, so the algorithm performs only a constant number of operations and uses a constant amount of memory. It comfortably fits the problem's time and memory limits.
Test Cases
import sys
import io
def solve():
first = input().strip()
second = input().strip()
wins = {
"Rock": {"Lizard", "Scissors"},
"Scissors": {"Paper", "Lizard"},
"Paper": {"Rock", "Spock"},
"Lizard": {"Spock", "Paper"},
"Spock": {"Scissors", "Rock"},
}
if first == second:
return "Tie"
if second in wins[first]:
return "First"
return "Second"
def run(inp: str) -> str:
global input
old_stdin = sys.stdin
old_input = input
try:
sys.stdin = io.StringIO(inp)
input = sys.stdin.readline
return solve()
finally:
sys.stdin = old_stdin
input = old_input
# Provided samples
assert run("Rock\nPaper\n") == "Second", "sample 1"
assert run("Rock\nRock\n") == "Tie", "sample 2"
assert run("Lizard\nSpock\n") == "First", "sample 3"
# All equal values
assert run("Spock\nSpock\n") == "Tie", "equal moves"
# Reverse direction of a winning relationship
assert run("Paper\nRock\n") == "First", "Paper defeats Rock"
assert run("Rock\nPaper\n") == "Second", "Paper defeats Rock"
# Second winning relationship of a move
assert run("Spock\nRock\n") == "Second", "Rock defeats Spock"
assert run("Spock\nScissors\n") == "First", "Spock defeats Scissors"
# Lizard's two different winning relationships
assert run("Lizard\nPaper\n") == "First", "Lizard defeats Paper"
assert run("Paper\nLizard\n") == "Second", "Lizard defeats Paper"
| Test input | Expected output | What it validates |
|---|---|---|
Spock / Spock |
Tie |
Equality handling for another move |
Paper / Rock |
First |
One direction of a winning relationship |
Spock / Rock |
Second |
The second player winning with the first move's opponent |
Lizard / Paper |
First |
The second winning edge of Lizard |
Paper / Lizard |
Second |
Reversing the same relationship |
The problem does not actually have a separate minimum-size or maximum-size parameter. Each test case always contains exactly two moves, so the relevant boundary is the complete set of five possible values. The tests above cover all structural cases, including equal moves and both directions of several relationships.
Edge Cases
The first edge case is equality. For
Rock
Rock
the algorithm reads both moves, finds that first == second, and immediately returns Tie. It does not attempt to treat Rock as defeating itself, because self-comparisons are excluded by the game rules.
The second edge case is a move with two distinct ways to win. Consider
Lizard
Spock
The dictionary entry for Lizard is {Spock, Paper}. Since Spock is present, the condition second in wins[first] is true and the result is First. This catches implementations that remember only one of Lizard's two winning relationships.
The third edge case is the reversed pair
Paper
Lizard
The entry for Paper is {Rock, Spock}, so Lizard is not present. The moves are not equal, so the algorithm reaches the final branch and prints Second. This confirms that the relation is directional and cannot be treated as an undirected connection.
The fourth edge case is Spock's less obvious interaction with Rock:
Spock
Rock
Rock appears in wins["Spock"], so the algorithm prints First. Reversing the input to
Rock
Spock
makes the lookup fail, and the algorithm prints Second. These two inputs together verify that the direction of every relationship is being interpreted correctly.