CF 146A - Lucky Ticket
We are asked to determine whether a ticket number is lucky. A lucky ticket number satisfies two conditions simultaneously. First, every digit of the number must be either 4 or 7.
Rating: 800
Tags: implementation
Solve time: 1m 10s
Verified: yes
Solution
Problem Understanding
We are asked to determine whether a ticket number is lucky. A lucky ticket number satisfies two conditions simultaneously. First, every digit of the number must be either 4 or 7. Second, the sum of the digits in the first half of the number must equal the sum of the digits in the second half. The input provides an even integer n, the length of the ticket number, followed by the ticket number itself as a string of digits. The output should be "YES" if the ticket is lucky, otherwise "NO".
The constraints are mild: the ticket length n is between 2 and 50, always even. Since we only need to inspect each digit individually and sum halves of the array, an O(n) algorithm is sufficient. There is no need for anything more complex. Potential edge cases include tickets with leading zeros, tickets containing a mix of lucky and non-lucky digits, and the minimal-size ticket of length 2, where the halves are just single digits.
A naive approach that sums the halves but forgets to check for non-lucky digits would fail. For example, the input:
2
47
is not lucky because the first half sum (4) does not equal the second half sum (7). If the code only checked for lucky digits but not the sum, it might incorrectly accept it. Similarly, a ticket like:
4
4444
satisfies the lucky digits condition, and both halves sum to 8, so it is lucky.
Approaches
The brute-force method would be to iterate over all digits twice: once to ensure all digits are either 4 or 7, and once to compute the sum of each half and compare them. This works because the operations per digit are constant. The complexity is O(n), which is acceptable given the constraints, so there is no need to optimize further.
The key insight is that the problem is linear by nature. Checking digit validity and summing halves can be done in a single pass. The halves can be identified by slicing the string into the first n/2 digits and the second n/2 digits. Each step is independent, so we do not require additional data structures or complex algorithms. The brute-force approach is effectively the optimal approach in this scenario.
| Approach | Time Complexity | Space Complexity | Verdict |
|---|---|---|---|
| Brute Force | O(n) | O(1) | Accepted |
| Optimal | O(n) | O(1) | Accepted |
Algorithm Walkthrough
- Read the input values: the integer n and the ticket number as a string. The string format is essential to handle potential leading zeros.
- Initialize two sums,
sum_firstandsum_second, to zero. These will hold the sums of the first and second halves of the ticket. - Iterate over the ticket number. For each digit, check if it is either '4' or '7'. If any digit is not a lucky digit, immediately output "NO" and terminate. This ensures the first lucky-number condition is enforced.
- For the first n/2 digits, convert each character to an integer and add it to
sum_first. - For the remaining n/2 digits, convert each character to an integer and add it to
sum_second. - After processing all digits, compare
sum_firstandsum_second. If they are equal, output "YES"; otherwise, output "NO".
Why it works: The algorithm maintains the invariant that any non-lucky digit triggers immediate rejection. The sum calculations are exact and separated by halves. Since both conditions are evaluated independently and sequentially, the final decision is correct for any valid input length within the constraints.
Python Solution
import sys
input = sys.stdin.readline
n = int(input())
ticket = input().strip()
half = n // 2
sum_first = 0
sum_second = 0
for i in range(n):
if ticket[i] not in '47':
print("NO")
sys.exit()
if i < half:
sum_first += int(ticket[i])
else:
sum_second += int(ticket[i])
if sum_first == sum_second:
print("YES")
else:
print("NO")
The code first reads the ticket length and the ticket number. It calculates the midpoint for splitting the number. Iterating over all digits, it first validates that each digit is lucky. Then it sums the two halves separately using a simple index comparison. The sys.exit() after printing "NO" ensures we immediately terminate if a non-lucky digit is found. Finally, the equality of the halves is checked to decide the output.
Worked Examples
Example 1
Input:
2
47
| i | ticket[i] | is lucky? | sum_first | sum_second |
|---|---|---|---|---|
| 0 | 4 | yes | 4 | 0 |
| 1 | 7 | yes | 4 | 7 |
The sum of halves is 4 vs 7. Output is "NO". This confirms that the sum condition is correctly enforced.
Example 2
Input:
4
4477
| i | ticket[i] | is lucky? | sum_first | sum_second |
|---|---|---|---|---|
| 0 | 4 | yes | 4 | 0 |
| 1 | 4 | yes | 8 | 0 |
| 2 | 7 | yes | 8 | 7 |
| 3 | 7 | yes | 8 | 14 |
Sum comparison 8 vs 14 fails. Output is "NO". The code correctly handles longer tickets and ensures that sums match.
Example 3
Input:
4
4747
| i | ticket[i] | is lucky? | sum_first | sum_second |
|---|---|---|---|---|
| 0 | 4 | yes | 4 | 0 |
| 1 | 7 | yes | 11 | 0 |
| 2 | 4 | yes | 11 | 4 |
| 3 | 7 | yes | 11 | 11 |
Sum comparison 11 vs 11 passes. Output is "YES". This demonstrates the algorithm correctly identifies a lucky ticket.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n) | Each digit is inspected exactly once to check for lucky digits and to update the sums. |
| Space | O(1) | Only a few integer variables are used; no additional data structures scale with n. |
The constraints allow n up to 50, making an O(n) solution trivial for the 2-second limit. Memory usage is minimal.
Test Cases
import sys, io
def run(inp: str) -> str:
sys.stdin = io.StringIO(inp)
from contextlib import redirect_stdout
out = io.StringIO()
with redirect_stdout(out):
exec(open(__file__).read(), {'sys': sys})
return out.getvalue().strip()
# Provided samples
assert run("2\n47\n") == "NO", "sample 1"
assert run("4\n4747\n") == "YES", "sample 2"
# Custom cases
assert run("4\n4477\n") == "NO", "sum mismatch"
assert run("2\n44\n") == "YES", "minimal length lucky"
assert run("2\n40\n") == "NO", "non-lucky digit"
assert run("6\n447477\n") == "YES", "even split, sums equal"
assert run("6\n447477\n") == "YES", "leading zeros check not needed since input digits always in '0-9'"
| Test input | Expected output | What it validates |
|---|---|---|
| 4\n4477 | NO | Checks sum mismatch with all lucky digits |
| 2\n44 | YES | Minimal-length ticket that is lucky |
| 2\n40 | NO | Non-lucky digit rejected |
| 6\n447477 | YES | Longer ticket with even split sums matching |
Edge Cases
For a ticket like 02 with n=2, the code rejects it immediately because '0' is not a lucky digit, demonstrating correct handling of leading zeros. For the minimal input 44 with n=2, the first and second halves sum to 4 each, producing "YES". For maximal length, the algorithm still iterates at most 50 digits, performing simple arithmetic, ensuring correctness without overflow or off-by-one errors.