#include <bits/stdc++.h>
using namespace std;
using ll = long long;
/*
QUESTION IN SIMPLE WORDS
------------------------
We start at top-left and want to reach bottom-right.
Allowed moves:
- left
- right
- down
Not allowed:
- up
- revisiting a cell
- entering blocked cells
Goal:
maximize number of cells visited on a valid path
Grid format in this code:
'.' = open
'#' = blocked
------------------------------------------------------------
BRUTE FORCE IDEA
------------------------------------------------------------
Use backtracking:
- try all valid paths
- never revisit a cell
- stop when we reach bottom-right
- keep the longest valid path
------------------------------------------------------------
WHY BRUTE FORCE IS TOO SLOW
------------------------------------------------------------
The number of possible paths grows very quickly.
Backtracking becomes too slow.
------------------------------------------------------------
OPTIMIZED IDEA
------------------------------------------------------------
Process row by row.
Inside one continuous open segment of a row:
- we can move left and right
- then choose where to go down
So we use DP.
cur[c] =
best answer after processing current row and standing at column c
For every open segment, we compute:
- best sweep from left
- best sweep from right
Then we try dropping down into the next row.
*/
int maxVisitedCells(vector<string> g) {
int n = g.size();
int m = g[0].size();
if (g[0][0] == '#' || g[n - 1][m - 1] == '#') return -1;
const ll NEG = -(1LL << 60);
vector<ll> cur(m, NEG), nxt(m, NEG);
// Start cell itself counts as visited
cur[0] = 1;
for (int r = 0; r < n; r++) {
// Last row: only need to see if target can be reached in this row
if (r == n - 1) {
ll best = NEG;
int c = 0;
while (c < m) {
if (g[r][c] == '#') {
c++;
continue;
}
// Find one continuous open segment [L..R]
int L = c;
while (c < m && g[r][c] == '.') c++;
int R = c - 1;
vector<ll> pref(R - L + 1, NEG), suff(R - L + 1, NEG);
// Best sweep from left
ll run = NEG;
for (int j = L; j <= R; j++) {
if (cur[j] != NEG) run = max(run, cur[j] - j);
pref[j - L] = run;
}
// Best sweep from right
run = NEG;
for (int j = R; j >= L; j--) {
if (cur[j] != NEG) run = max(run, cur[j] + j);
suff[j - L] = run;
}
// If destination column is inside this segment, try to reach it
if (L <= m - 1 && m - 1 <= R) {
ll cand = NEG;
if (pref[m - 1 - L] != NEG) cand = max(cand, pref[m - 1 - L] + (m - 1));
if (suff[m - 1 - L] != NEG) cand = max(cand, suff[m - 1 - L] - (m - 1));
best = max(best, cand);
}
}
return best == NEG ? -1 : (int)best;
}
fill(nxt.begin(), nxt.end(), NEG);
int c = 0;
while (c < m) {
if (g[r][c] == '#') {
c++;
continue;
}
// Continuous open segment [L..R]
int L = c;
while (c < m && g[r][c] == '.') c++;
int R = c - 1;
vector<ll> pref(R - L + 1, NEG), suff(R - L + 1, NEG);
// Best way to sweep this segment from left
ll run = NEG;
for (int j = L; j <= R; j++) {
if (cur[j] != NEG) run = max(run, cur[j] - j);
pref[j - L] = run;
}
// Best way to sweep this segment from right
run = NEG;
for (int j = R; j >= L; j--) {
if (cur[j] != NEG) run = max(run, cur[j] + j);
suff[j - L] = run;
}
// Try going down from each column in this segment
for (int j = L; j <= R; j++) {
if (g[r + 1][j] == '#') continue;
ll best = NEG;
if (pref[j - L] != NEG) best = max(best, pref[j - L] + j);
if (suff[j - L] != NEG) best = max(best, suff[j - L] - j);
// +1 because cell below is newly visited
if (best != NEG) nxt[j] = max(nxt[j], best + 1);
}
}
cur.swap(nxt);
}
return -1;
}
int main() {
vector<string> g = {
"...",
".#.",
"..."
};
cout << maxVisitedCells(g) << '\n';
return 0;
}