1+ import java .io .ByteArrayInputStream ;
2+ import java .io .EOFException ;
13import java .io .IOException ;
24import java .io .InputStream ;
35import java .io .OutputStream ;
6+ import java .nio .charset .StandardCharsets ;
7+ import java .util .ArrayDeque ;
8+ import java .util .Deque ;
49
510/*
6- Принцип работы алгоритма:
7- - Выражение в обратной польской нотации читается слева направо по токенам (разделитель — пробел).
8- - Используется стек целых чисел.
9- * Если токен — число, кладём его в стек.
10- * Если токен — операция (+, -, *, /), достаём два верхних значения b и a (сначала b, потом a),
11- вычисляем a (op) b и результат кладём обратно в стек.
12- - После обработки всех токенов ответ — верхний элемент стека (даже если элементов больше одного).
11+ Принцип:
12+ - Читаем токены ОПН слева направо.
13+ - Число -> push в стек.
14+ - Операция -> pop b, pop a, считаем a op b, push результат.
15+ - Ответ: верх стека.
1316
1417Корректность:
15- - Инвариант: после обработки первых i токенов стек содержит значения всех уже полностью вычисленных
16- подвыражений, которые по правилам ОПН должны ждать дальнейших операций.
17- - При чтении числа инвариант сохраняется: число становится готовым операндом и помещается в стек.
18- - При чтении операции по определению ОПН она применяется к двум ближайшим слева операндам,
19- которые и лежат на вершине стека в нужном порядке добавления. Мы снимаем b и a и кладём результат,
20- тем самым заменяя эти два операнда на значение их подвыражения — инвариант сохраняется.
21- - Для деления используется Math.floorDiv(a, b), что соответствует требованию “округление вниз” (floor).
22- По условию деления на отрицательное число нет, но a может быть отрицательным — floorDiv это корректно обрабатывает.
23-
24- Оценка сложности:
25- - Время: O(m), где m — число токенов (каждый токен обрабатывается за O(1)).
26- - Память: O(s), где s — максимальный размер стека (в худшем случае O(m)).
18+ - В ОПН операция применяется к двум ближайшим слева операндам; к моменту чтения операции они лежат
19+ на вершине стека. Мы заменяем эти два значения на результат, сохраняя корректное состояние.
20+ - Деление требуется “вниз” (floor), поэтому используем Math.floorDiv(a, b).
21+
22+ Сложность:
23+ - Время O(m), m — число токенов.
24+ - Память O(s), s — максимальный размер стека (в худшем O(m)).
2725*/
2826public class Calculator {
2927
30- // -------------------- SOLUTION --------------------
31- static int evalRpn (FastIn in ) throws IOException {
32- IntStack st = new IntStack (64 );
28+ private static int eval (FastIn in ) throws IOException {
29+ Deque <Integer > st = new ArrayDeque <>();
30+
31+ while (true ) {
32+ String t ;
33+ try {
34+ t = in .next ();
35+ } catch (EOFException e ) {
36+ break ;
37+ }
3338
34- String tok ;
35- while ((tok = in .nextOrNull ()) != null ) {
36- if (tok .length () == 1 ) {
37- char op = tok .charAt (0 );
39+ if (t .length () == 1 ) {
40+ char op = t .charAt (0 );
3841 if (op == '+' || op == '-' || op == '*' || op == '/' ) {
3942 int b = st .pop ();
4043 int a = st .pop ();
@@ -54,45 +57,19 @@ static int evalRpn(FastIn in) throws IOException {
5457 continue ;
5558 }
5659 }
57- st .push (Integer .parseInt (tok ));
58- }
59-
60- return st .peek ();
61- }
62-
63- // -------------------- INT STACK --------------------
64- static final class IntStack {
65- private int [] a ;
66- private int sz ;
6760
68- IntStack (int cap ) {
69- this .a = new int [Math .max (2 , cap )];
70- this .sz = 0 ;
61+ st .push (Integer .parseInt (t ));
7162 }
7263
73- void push (int v ) {
74- if (sz == a .length ) {
75- int [] b = new int [a .length << 1 ];
76- System .arraycopy (a , 0 , b , 0 , a .length );
77- a = b ;
78- }
79- a [sz ++] = v ;
80- }
81-
82- int pop () {
83- return a [--sz ];
84- }
85-
86- int peek () {
87- return a [sz - 1 ];
88- }
64+ return st .peek ();
8965 }
9066
9167 // -------------------- FAST INPUT --------------------
9268 static final class FastIn {
9369 private final InputStream in ;
9470 private final byte [] buf = new byte [1 << 16 ];
95- private int ptr = 0 , len = 0 ;
71+ private int ptr = 0 ;
72+ private int len = 0 ;
9673
9774 FastIn (InputStream in ) {
9875 this .in = in ;
@@ -109,12 +86,12 @@ private int read() throws IOException {
10986 return buf [ptr ++];
11087 }
11188
112- String nextOrNull () throws IOException {
89+ String next () throws IOException {
11390 int c ;
11491 do {
11592 c = read ();
11693 if (c == -1 ) {
117- return null ;
94+ throw new EOFException ( "EOF" ) ;
11895 }
11996 } while (c <= ' ' );
12097
@@ -187,38 +164,30 @@ private static void run() throws Exception {
187164 FastIn in = new FastIn (System .in );
188165 FastOut out = new FastOut (System .out );
189166
190- int ans = evalRpn (in );
167+ int ans = eval (in );
191168
192169 out .writeInt (ans );
193170 out .writeByte ('\n' );
194171 out .flush ();
195172 }
196173
174+ // -------------------- LOCAL TESTS --------------------
197175 private static void test () throws Exception {
198176 assertEq (9 , evalFromString ("2 1 + 3 *" ));
199177 assertEq (38 , evalFromString ("7 2 + 4 * 2 +" ));
200-
201- // деление “вниз”
202178 assertEq (-1 , evalFromString ("-1 3 /" ));
203179 assertEq (-2 , evalFromString ("-4 3 /" ));
204-
205- // порядок операндов
206180 assertEq (2 , evalFromString ("10 2 4 * -" ));
207-
208- // одно число
209181 assertEq (5 , evalFromString ("5" ));
210-
211182 System .out .println ("Test OK" );
212183 }
213184
214185 private static int evalFromString (String s ) throws Exception {
215- InputStream is = new java .io .ByteArrayInputStream (
216- s .getBytes (java .nio .charset .StandardCharsets .US_ASCII )
217- );
218- return evalRpn (new FastIn (is ));
186+ InputStream is = new ByteArrayInputStream (s .getBytes (StandardCharsets .US_ASCII ));
187+ return eval (new FastIn (is ));
219188 }
220189
221- static void assertEq (int exp , int act ) {
190+ private static void assertEq (int exp , int act ) {
222191 if (exp != act ) {
223192 throw new AssertionError ("Expected=" + exp + ", actual=" + act );
224193 }
0 commit comments