#include <iostream>
#include <algorithm>
#include <array>
#include <queue>
#include <numeric>
template<typename T>
class NumIter
{
public:
using iterator_category = std::random_access_iterator_tag;
using value_type = T;
using difference_type = std::int64_t;
using pointer = T;
using reference = T;
NumIter(const T x)
: value(x)
{
}
T operator*() const
{
return value;
}
NumIter& operator++()
{
++value;
return *this;
}
NumIter operator++(int)
{
const NumIter old{value};
++value;
return old;
}
NumIter& operator--()
{
--value;
return *this;
}
NumIter operator--(int)
{
const NumIter old{value};
--value;
return old;
}
NumIter& operator+=(const std::int64_t n)
{
value += n;
return *this;
}
auto operator-(const NumIter& other) const
{
return static_cast<difference_type>(value) -
static_cast<difference_type>(other.value);
}
bool operator==(const NumIter& other) const
{
return value == other.value;
}
private:
T value = {};
};
static int R;
static int C;
struct Room
{
bool operator==(const Room& other) const
{
return x == other.x && y == other.y;
}
bool valid() const
{
return 0 <= x && x < R && 0 <= y && y < C;
}
std::array<Room, 4> neighbours() const
{
const std::array<Room, 4> res = {
{{x, y - 1}, {x, y + 1}, {x - 1, y}, {x + 1, y}}};
return res;
}
int x, y;
};
bool DFS(const Room src, const Room dest, std::vector<std::vector<char>>& visited,
const std::vector<std::vector<char>>& mat)
{
visited[src.x][src.y] = true;
if(src == dest)
{
return true;
}
for(const Room& neigh : src.neighbours())
{
if(!neigh.valid() || visited[neigh.x][neigh.y] ||
mat[neigh.x][neigh.y] == '*' || mat[neigh.x][neigh.y] == 'D')
{
continue;
}
if(DFS(neigh, dest, visited, mat) == true)
{
return true;
}
}
return false;
}
void DFS_fill(const Room src, int current_depth, const int g_max_depth,
std::vector<std::vector<char>>& visited, std::vector<std::vector<char>>& mat)
{
if(mat[src.x][src.y] == 'D')
{
current_depth = g_max_depth;
}
if(current_depth < 0)
{
return;
}
visited[src.x][src.y] = true;
mat[src.x][src.y] = 'D';
for(const Room& neigh : src.neighbours())
{
if(!neigh.valid() || visited[neigh.x][neigh.y])
{
continue;
}
DFS_fill(neigh, current_depth - 1, g_max_depth, visited, mat);
}
}
bool check_for_k(const Room src, const Room dest, const int K,
const std::vector<Room>& dragons_pos, std::vector<std::vector<char>> mat)
{
std::vector<std::vector<char>> visited(R, std::vector<char>(C, false));
for(const Room& r : dragons_pos)
{
DFS_fill(r, K - 1, K - 1, visited, mat);
}
visited = std::vector<std::vector<char>>(R, std::vector<char>(C, false));
bool res = DFS(src, dest, visited, mat);
return !res;
}
int main()
{
std::freopen("barbar.in", "r", stdin);
std::freopen("barbar.out", "w", stdout);
std::cin >> R >> C;
std::vector<std::vector<char>> mat(R, std::vector<char>(C));
std::vector<Room> dragons_pos;
Room src;
Room dest;
for(int i = 0; i < R; ++i)
{
for(int j = 0; j < C; ++j)
{
std::cin >> mat[i][j];
if(mat[i][j] == 'I')
{
src = {i, j};
}
if(mat[i][j] == 'O')
{
dest = {i, j};
}
if(mat[i][j] == 'D')
{
dragons_pos.push_back(Room{i, j});
}
}
}
int res =
*std::lower_bound(NumIter<int>{1}, NumIter<int>{R * C}, true,
[&](const int i1, const int i2)
{
return check_for_k(src, dest, i1, dragons_pos, mat) < i2;
});
if(res == 1)
{
std::cout << -1 << '\n';
return 0;
}
--res;
std::cout << res << '\n';
}