/*
Problem: Minimum and Maximum Drops Needed to Fill the First Column
Company Tag: Trilogy OA
Problem Statement:
We are given a grid.
'.' means empty cell.
'#' means blocked cell.
In one move:
1. Choose a row.
2. Drop a block from the left.
3. It moves right until blocked or until the end.
4. Then it falls down until blocked or until the bottom.
5. It stops there.
We need to fill the first column with blocks.
Return:
minimum moves needed
maximum moves possible
Constraints:
1 <= field.length <= 12
1 <= field[0].length <= 12
field[i][j] is either '.' or '#'
The maximum number of moves will not exceed 12.
Simple Brute Force:
Try all possible row choices.
Why This Works:
The grid is small.
Also, the maximum number of moves is guaranteed to be at most 12.
Better Idea:
Use DFS with memoization.
For every board state:
- if first column is full, return {0, 0}
- otherwise, try dropping a block in every row
- recursively calculate minimum and maximum moves
Dry Run:
field =
[
['.', '#'],
['.', '.']
]
Minimum:
Drop in row 0 twice.
Maximum:
Drop in row 1 once,
then row 1 again,
then row 0 once.
Answer = [2, 3]
Time Complexity:
O(number of reachable states * rows)
Space Complexity:
O(number of reachable states)
*/
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
int rows;
int cols;
const int INF = 1e9;
unordered_map<string, pair<int, int>> memo;
string encode(vector<vector<char>>& field) {
string state;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
state.push_back(field[i][j]);
}
}
return state;
}
bool firstColumnFull(vector<vector<char>>& field) {
for (int i = 0; i < rows; i++) {
if (field[i][0] == '.') {
return false;
}
}
return true;
}
vector<vector<char>> dropBlock(vector<vector<char>> field, int row) {
// If the leftmost cell is already blocked, this move changes nothing.
if (field[row][0] == '#') {
return field;
}
int r = row;
int c = 0;
// First, the block slides to the right.
while (c + 1 < cols && field[r][c + 1] == '.') {
c++;
}
// Then, it falls down.
while (r + 1 < rows && field[r + 1][c] == '.') {
r++;
}
field[r][c] = '#';
return field;
}
pair<int, int> dfs(vector<vector<char>>& field, int movesLeft) {
if (firstColumnFull(field)) {
return {0, 0};
}
if (movesLeft == 0) {
return {INF, -INF};
}
string key = encode(field) + "|" + to_string(movesLeft);
if (memo.find(key) != memo.end()) {
return memo[key];
}
int bestMinimum = INF;
int bestMaximum = -INF;
string before = encode(field);
for (int row = 0; row < rows; row++) {
vector<vector<char>> nextField = dropBlock(field, row);
string after = encode(nextField);
// Ignore a row choice if it does not change the board.
if (before == after) {
continue;
}
pair<int, int> child = dfs(nextField, movesLeft - 1);
if (child.first != INF) {
bestMinimum = min(bestMinimum, 1 + child.first);
}
if (child.second != -INF) {
bestMaximum = max(bestMaximum, 1 + child.second);
}
}
return memo[key] = {bestMinimum, bestMaximum};
}
vector<int> solution(vector<vector<char>> field) {
rows = field.size();
cols = field[0].size();
pair<int, int> answer = dfs(field, 12);
return {answer.first, answer.second};
}
};
int main() {
vector<vector<char>> field = {
{'.', '#'},
{'.', '.'}
};
Solution obj;
vector<int> answer = obj.solution(field);
cout << answer[0] << " " << answer[1] << endl;
return 0;
}