C# solution
\npublic class Solution {\n    \n    private readonly IList<int[]> _directions = new List<int[]>()\n    {\n        new int[] { 1, 0 },\n        new int[] { -1, 
```\npublic class Solution {\n    \n    private readonly IList<int[]> _directions = new List<int[]>()\n    {\n        new int[] { 1, 0 },\n        new int[] { -1, 0 },\n        new int[] { 0, 1 },            \n        new int[] { 0, -1 }\n    };\n    \n    public int CutOffTree(IList<IList<int>> forest) {\n        \n        if (forest == null)\n        {\n            throw new ArgumentNullException("Parameter cannot be null", nameof(forest));\n        }\n        \n        return CutOffTreeBfs(forest);\n    }\n    \n    private int CutOffTreeBfs(IList<IList<int>> forest)\n    {\n        // Note that here we will store <height:coordinates>\n        var treeMap = new Dictionary<int, int[]>();\n        for (var row = 0; row < forest.Count; row++)\n        {\n            for (var col = 0; col < forest[0].Count; col++)\n            {\n                // Note that we need to store only trees of height greater than one, since anything else is not a tree and either just grass or a barrier\n                if (forest[row][col] > 1)\n                {\n                    // Note that we are guaranteed unique heights\n                    treeMap.Add(forest[row][col], new int[] { row, col });\n                }\n            }\n        }\n        \n        var result = 0;\n        var startRow = 0;\n        var startCol = 0;\n        var sortedTreeMap = treeMap.OrderBy(kvp => kvp.Key);\n        \n        foreach (var kvp in sortedTreeMap)\n        {\n            var endRow = kvp.Value[0];\n            var endCol = kvp.Value[1];\n            \n            var steps = GetShortestPathBfs(forest, startRow, startCol, endRow, endCol);\n            if (steps == -1)\n            {\n                return -1;\n            }\n            \n            result += steps;\n            startRow = endRow;\n            startCol = endCol;\n        }\n                        \n        return result;\n    }\n    \n    private int GetShortestPathBfs(IList<IList<int>> forest, int startRow, int startCol, int endRow, int endCol)\n    {\n        if (startRow == endRow && startCol == endCol)\n        {\n            return 0;\n        }\n        \n        var queue = new Queue<int[]>();\n        queue.Enqueue(new int[] { startRow, startCol });\n        \n        var visited = new int[forest.Count, forest[0].Count];\n        visited[startRow, startCol] = 1;\n        \n        var level = 0;\n        \n        while (queue.Count > 0)\n        {\n            level++;\n            var size = queue.Count;\n            while (size > 0)\n            {\n                var oldNode = queue.Dequeue();\n                var oldRow = oldNode[0];\n                var oldCol = oldNode[1];\n                                \n                foreach (var d in _directions)\n                {\n                    var newRow = oldRow + d[0];\n                    var newCol = oldCol + d[1];\n                    \n                    if (newRow >= 0 && newRow < forest.Count && newCol >= 0 && newCol < forest[0].Count && forest[newRow][newCol] != 0 && visited[newRow, newCol] == 0)\n                    { \n                        if (newRow == endRow && newCol == endCol)\n                        {\n                            return level;\n                        }                        \n                        \n                        visited[newRow, newCol] = 1;                        \n                        queue.Enqueue(new int[] { newRow, newCol });\n                    }\n                }\n                \n                size--;\n            }\n        }\n        \n        return -1;\n    }\n        \n}\n```
[]