1+ #include < bits/stdc++.h>
2+ using namespace std ;
3+
4+ static uint64_t splitmix64 (uint64_t x) {
5+ x += 0x9e3779b97f4a7c15ULL ;
6+ x = (x ^ (x >> 30 )) * 0xbf58476d1ce4e5b9ULL ;
7+ x = (x ^ (x >> 27 )) * 0x94d049bb133111ebULL ;
8+ return x ^ (x >> 31 );
9+ }
10+
11+ enum Act { LEFT = 0 , RIGHT = 1 , STEP = 2 };
12+
13+ int main () {
14+ ios::sync_with_stdio (false );
15+ cin.tie (nullptr );
16+
17+ int r, c;
18+ if (!(cin >> r >> c)) return 0 ;
19+ vector<string> g (r);
20+ for (int i = 0 ; i < r; i++) cin >> g[i];
21+
22+ vector<vector<int >> cellId (r, vector<int >(c, -1 ));
23+ vector<int > cellRow, cellCol;
24+ cellRow.reserve (r * c);
25+ cellCol.reserve (r * c);
26+
27+ for (int i = 0 ; i < r; i++) {
28+ for (int j = 0 ; j < c; j++) {
29+ if (g[i][j] == ' .' ) {
30+ int id = (int )cellRow.size ();
31+ cellId[i][j] = id;
32+ cellRow.push_back (i);
33+ cellCol.push_back (j);
34+ }
35+ }
36+ }
37+
38+ int M = (int )cellRow.size ();
39+ if (M == 0 ) return 0 ;
40+
41+ vector<array<int ,4 >> stateOfCell (M);
42+ int N = M * 4 ;
43+ for (int cell = 0 ; cell < M; cell++) {
44+ for (int d = 0 ; d < 4 ; d++) stateOfCell[cell][d] = cell * 4 + d;
45+ }
46+
47+ static const int dx[4 ] = {-1 , 0 , 1 , 0 };
48+ static const int dy[4 ] = {0 , 1 , 0 , -1 };
49+
50+ vector<int > distWall (N, 0 );
51+ vector<int > leftT (N), rightT (N), stepT (N);
52+ vector<int > cellOfState (N), dirOfState (N);
53+
54+ auto isWall = [&](int x, int y) -> bool {
55+ if (x < 0 || x >= r || y < 0 || y >= c) return true ;
56+ return g[x][y] == ' #' ;
57+ };
58+
59+ for (int cell = 0 ; cell < M; cell++) {
60+ int x0 = cellRow[cell], y0 = cellCol[cell];
61+ for (int d = 0 ; d < 4 ; d++) {
62+ int s = stateOfCell[cell][d];
63+ cellOfState[s] = cell;
64+ dirOfState[s] = d;
65+
66+ leftT[s] = stateOfCell[cell][(d + 3 ) & 3 ];
67+ rightT[s] = stateOfCell[cell][(d + 1 ) & 3 ];
68+
69+ int nx = x0 + dx[d], ny = y0 + dy[d];
70+ if (!isWall (nx, ny)) {
71+ int ncell = cellId[nx][ny];
72+ stepT[s] = stateOfCell[ncell][d];
73+ } else {
74+ stepT[s] = -1 ;
75+ }
76+
77+ int cnt = 0 ;
78+ int x = x0, y = y0;
79+ while (true ) {
80+ int tx = x + dx[d], ty = y + dy[d];
81+ if (isWall (tx, ty)) break ;
82+ cnt++;
83+ x = tx; y = ty;
84+ if (cnt >= 99 ) break ;
85+ }
86+ distWall[s] = cnt;
87+ }
88+ }
89+
90+ // Compute observational equivalence classes (Moore machine minimization with step disabled when dist==0).
91+ vector<int > cls (N, 0 ), newcls (N, 0 );
92+ vector<int > distToClass (100 , -1 );
93+ int initClasses = 0 ;
94+ for (int s = 0 ; s < N; s++) {
95+ int d = distWall[s];
96+ if (distToClass[d] == -1 ) distToClass[d] = initClasses++;
97+ cls[s] = distToClass[d];
98+ }
99+
100+ while (true ) {
101+ unordered_map<uint64_t , int > mp;
102+ mp.reserve ((size_t )N * 2 );
103+
104+ int nextId = 0 ;
105+ bool same = true ;
106+ for (int s = 0 ; s < N; s++) {
107+ int out = distWall[s];
108+ int l = cls[leftT[s]];
109+ int rcls = cls[rightT[s]];
110+ int st = (distWall[s] > 0 ? cls[stepT[s]] : -1 );
111+
112+ uint64_t key = ((uint64_t )out << 48 ) |
113+ ((uint64_t )l << 32 ) |
114+ ((uint64_t )rcls << 16 ) |
115+ (uint64_t )(st + 1 );
116+
117+ auto it = mp.find (key);
118+ int id;
119+ if (it == mp.end ()) {
120+ id = nextId++;
121+ mp.emplace (key, id);
122+ } else {
123+ id = it->second ;
124+ }
125+ newcls[s] = id;
126+ if (newcls[s] != cls[s]) same = false ;
127+ }
128+ cls.swap (newcls);
129+ if (same) break ;
130+ }
131+
132+ int numClasses = 0 ;
133+ for (int s = 0 ; s < N; s++) numClasses = max (numClasses, cls[s] + 1 );
134+
135+ vector<int > cellMark (M, 0 );
136+ int cellMarkTag = 1 ;
137+
138+ vector<int > classStamp (numClasses, 0 ), classCell (numClasses, -1 );
139+ int classTag = 1 ;
140+
141+ vector<int > tmpCellStamp (M, 0 );
142+ int tmpCellTag = 1 ;
143+
144+ unordered_map<uint64_t , int > seenBeliefs;
145+ seenBeliefs.reserve (1 << 16 );
146+
147+ vector<int > belief (N);
148+ iota (belief.begin (), belief.end (), 0 );
149+
150+ auto beliefHash = [&](const vector<int >& B, int d) -> uint64_t {
151+ uint64_t h = splitmix64 ((uint64_t )B.size () ^ (uint64_t )(d + 1234567 ));
152+ for (int s : B) h ^= splitmix64 ((uint64_t )s + 0x9e3779b97f4a7c15ULL );
153+ return h;
154+ };
155+
156+ auto actStr = [&](int act) -> const char * {
157+ if (act == LEFT) return " left" ;
158+ if (act == RIGHT) return " right" ;
159+ return " step" ;
160+ };
161+
162+ auto trans = [&](int s, int act) -> int {
163+ if (act == LEFT) return leftT[s];
164+ if (act == RIGHT) return rightT[s];
165+ return stepT[s];
166+ };
167+
168+ auto chooseAction = [&](const vector<int >& B, int curd, uint64_t bh) -> int {
169+ struct Cand {
170+ int act;
171+ int worstCells;
172+ int worstStates;
173+ int groups;
174+ int pref;
175+ };
176+
177+ array<vector<int >, 100 > buckets;
178+ array<int , 100 > usedFlags{};
179+ vector<int > used;
180+ used.reserve (100 );
181+
182+ auto evalAct = [&](int act) -> Cand {
183+ used.clear ();
184+ // We'll lazily clear buckets only when first used per dist.
185+ for (int dd : used) { (void )dd; } // no-op; used cleared.
186+
187+ int worstCells = 0 , worstStates = 0 , groups = 0 ;
188+
189+ for (int s : B) {
190+ int ns = trans (s, act);
191+ int dd = distWall[ns];
192+ if (!usedFlags[dd]) {
193+ usedFlags[dd] = 1 ;
194+ used.push_back (dd);
195+ buckets[dd].clear ();
196+ }
197+ buckets[dd].push_back (cellOfState[ns]);
198+ }
199+
200+ for (int dd : used) {
201+ int sz = (int )buckets[dd].size ();
202+ worstStates = max (worstStates, sz);
203+ groups++;
204+
205+ int tag = ++tmpCellTag;
206+ if (tmpCellTag == INT_MAX) {
207+ tmpCellTag = 1 ;
208+ fill (tmpCellStamp.begin (), tmpCellStamp.end (), 0 );
209+ tag = ++tmpCellTag;
210+ }
211+
212+ int uniq = 0 ;
213+ for (int cell : buckets[dd]) {
214+ if (tmpCellStamp[cell] != tag) {
215+ tmpCellStamp[cell] = tag;
216+ uniq++;
217+ }
218+ }
219+ worstCells = max (worstCells, uniq);
220+ }
221+
222+ for (int dd : used) usedFlags[dd] = 0 ;
223+
224+ int pref = 0 ;
225+ if (act == STEP) pref = 0 ;
226+ else if (act == RIGHT) pref = 1 ;
227+ else pref = 2 ;
228+
229+ return Cand{act, worstCells, worstStates, groups, pref};
230+ };
231+
232+ vector<int > acts;
233+ acts.push_back (LEFT);
234+ acts.push_back (RIGHT);
235+ if (curd > 0 ) acts.push_back (STEP);
236+
237+ vector<Cand> cands;
238+ cands.reserve (3 );
239+ for (int act : acts) cands.push_back (evalAct (act));
240+
241+ sort (cands.begin (), cands.end (), [](const Cand& a, const Cand& b) {
242+ if (a.worstCells != b.worstCells ) return a.worstCells < b.worstCells ;
243+ if (a.worstStates != b.worstStates ) return a.worstStates < b.worstStates ;
244+ if (a.groups != b.groups ) return a.groups > b.groups ;
245+ return a.pref < b.pref ;
246+ });
247+
248+ int cnt = ++seenBeliefs[bh];
249+ if ((cnt >= 6 ) && (int )cands.size () >= 2 ) {
250+ // Try to escape cycles by occasionally taking the 2nd best action.
251+ if (cnt % 6 == 0 ) return cands[1 ].act ;
252+ }
253+ return cands[0 ].act ;
254+ };
255+
256+ int d;
257+ while (cin >> d) {
258+ if (d == -1 ) return 0 ;
259+
260+ // Filter belief by observed distance.
261+ vector<int > nb;
262+ nb.reserve (belief.size ());
263+ for (int s : belief) if (distWall[s] == d) nb.push_back (s);
264+ belief.swap (nb);
265+
266+ if (belief.empty ()) {
267+ cout << " no" << endl;
268+ return 0 ;
269+ }
270+
271+ // Unique cell check.
272+ int tag = ++cellMarkTag;
273+ if (cellMarkTag == INT_MAX) {
274+ cellMarkTag = 1 ;
275+ fill (cellMark.begin (), cellMark.end (), 0 );
276+ tag = ++cellMarkTag;
277+ }
278+ int uniqueCell = -1 , cellCnt = 0 ;
279+ for (int s : belief) {
280+ int cell = cellOfState[s];
281+ if (cellMark[cell] != tag) {
282+ cellMark[cell] = tag;
283+ uniqueCell = cell;
284+ cellCnt++;
285+ if (cellCnt > 1 ) break ;
286+ }
287+ }
288+ if (cellCnt == 1 ) {
289+ cout << " yes " << (cellRow[uniqueCell] + 1 ) << " " << (cellCol[uniqueCell] + 1 ) << endl;
290+ return 0 ;
291+ }
292+
293+ // If two different cells are still possible but belong to the same equivalence class, impossible to resolve.
294+ int ctag = ++classTag;
295+ if (classTag == INT_MAX) {
296+ classTag = 1 ;
297+ fill (classStamp.begin (), classStamp.end (), 0 );
298+ ctag = ++classTag;
299+ }
300+ bool impossibleNow = false ;
301+ for (int s : belief) {
302+ int k = cls[s];
303+ int cell = cellOfState[s];
304+ if (classStamp[k] != ctag) {
305+ classStamp[k] = ctag;
306+ classCell[k] = cell;
307+ } else {
308+ if (classCell[k] != cell) {
309+ impossibleNow = true ;
310+ break ;
311+ }
312+ }
313+ }
314+ if (impossibleNow) {
315+ cout << " no" << endl;
316+ return 0 ;
317+ }
318+
319+ uint64_t bh = beliefHash (belief, d);
320+ int act = chooseAction (belief, d, bh);
321+
322+ cout << actStr (act) << endl;
323+
324+ // Apply action to belief (without next observation filtering yet).
325+ vector<int > after;
326+ after.reserve (belief.size ());
327+ if (act == STEP) {
328+ // Must be safe: only step when d>0
329+ if (d == 0 ) {
330+ cout << " no" << endl;
331+ return 0 ;
332+ }
333+ for (int s : belief) {
334+ int ns = stepT[s];
335+ if (ns < 0 ) { // should not happen if filtered correctly
336+ cout << " no" << endl;
337+ return 0 ;
338+ }
339+ after.push_back (ns);
340+ }
341+ } else if (act == LEFT) {
342+ for (int s : belief) after.push_back (leftT[s]);
343+ } else {
344+ for (int s : belief) after.push_back (rightT[s]);
345+ }
346+ belief.swap (after);
347+ }
348+
349+ return 0 ;
350+ }
0 commit comments