{"id":"dsa-dp-grid-paths","slug":"grid-paths","title":"Paths through a grid","text":"A floor-cleaning robot starts on the top-left cell of a grid of floor tiles, and it knows only two moves: one cell **right** or one cell **down**. Count the different routes that bring it to the bottom-right cell. The backtracking module counted paths by walking every one of them, which takes as long as there are paths, and a 20 × 20 grid has over 35 billion right-and-down paths. DP counts them without walking any.\n\nEvery route into a cell ends with a last move, and there are only two kinds: down out of the cell above, or right out of the cell to the left. No route is of both kinds, so the count for a cell is the two counts added up:\n\n```text\npaths[r][c] = paths[r - 1][c] + paths[r][c - 1]\n```\n\nA cell on the top row is reached only by moving right all the way, and a cell in the left column only by moving down all the way: one route each. Go through the rows from the top, and through each row from the left, and both cells a count needs are finished before it:\n\n```python\nrows, cols = 3, 5\npaths = [[0] * cols for _ in range(rows)]\nfor r in range(rows):\n    for c in range(cols):\n        if r == 0 or c == 0:\n            paths[r][c] = 1                                   # along an edge\n        else:\n            paths[r][c] = paths[r - 1][c] + paths[r][c - 1]   # above + left\nfor row in paths:\n    print(row)\n```\n\n```output\n[1, 1, 1, 1, 1]\n[1, 2, 3, 4, 5]\n[1, 3, 6, 10, 15]\n```\n\nThe bottom-right cell says 15. Each cell takes one addition, so a grid of rows × cols cells costs O(rows × cols) time and memory: 400 additions for the 20 × 20 grid, instead of 35 billion paths.\n\n### A table of lists: the trap\n\nThe table is built with `[[0] * cols for _ in range(rows)]`, a new list for every row. The shorter `[[0] * cols] * rows` looks the same, but it isn't. `*` repeats the **reference** to one inner list, so every row is the same list, as the references module showed:\n\n```pycon\n>>> table = [[0] * 3] * 2\n>>> table[0][0] = 7\n>>> table\n[[7, 0, 0], [7, 0, 0]]\n>>> table = [[0] * 3 for _ in range(2)]\n>>> table[0][0] = 7\n>>> table\n[[7, 0, 0], [0, 0, 0]]\n```\n\n`[0] * 3` on its own is fine: a number can't be changed in place, so sharing it does no harm. A list of lists needs the comprehension.\n\n### Walls\n\nNow some cells hold furniture, `#`, and the robot can't drive onto them. Such a wall cell gets 0 routes. Watch the edges: along the top row, the only way in is from the left, so one wall there cuts off the rest of that row, and the left column works the same way going down. Rather than writing those rules out, add up only the neighbors that exist:\n\n```python\ndef count_paths(grid):\n    rows, cols = len(grid), len(grid[0])\n    paths = [[0] * cols for _ in range(rows)]\n    for r in range(rows):\n        for c in range(cols):\n            if grid[r][c] == \"#\":\n                continue                              # a wall: 0 paths\n            if r == 0 and c == 0:\n                paths[r][c] = 1\n            else:\n                above = paths[r - 1][c] if r > 0 else 0\n                left = paths[r][c - 1] if c > 0 else 0\n                paths[r][c] = above + left\n    return paths[rows - 1][cols - 1]\n\n\nprint(count_paths([\".....\",\n                   \"..#..\",\n                   \".....\"]))\nprint(count_paths([\"...#.\",\n                   \".....\",\n                   \".....\"]))\n```\n\n```output\n6\n12\n```\n\nThe open 3 × 5 grid had 15 routes. In the first grid, the 9 that pass through the middle cell are gone. In the second, the 3 routes through the wall are gone, and the top-right cell is cut off with them: the only way into it was from the left, through the wall, so it gets 0 routes.\n\n### The cheapest path\n\nNow each cell has a cost, and you want the right-and-down path from corner to corner with the smallest total cost, both corners included. The same table works with another rule: the cheapest way into a cell comes from the cheaper of its two neighbors, so\n\n```text\nbest[r][c] = cost[r][c] + min(best[r - 1][c], best[r][c - 1])\n```\n\nOn the top row there's only the neighbor on the left, and in the left column only the one above. Trying every path instead takes as long as there are paths: exponential again.\n\nEach row of these tables reads only the row above it, so, as with the stairs, you could keep a single row and update it in place, for O(cols) memory. Keep the whole table when you want to read it afterward, for instance to find the path itself.\n","task":"This program fills the table of routes for a grid with two walls, and prints each row once it's filled. What does it print? Type each line it prints.\n","kind":"predict","seed":"grid = [\"....\",\n        \"..#.\",\n        \"#...\"]\npaths = [[0] * 4 for _ in range(3)]\nfor r in range(3):\n    for c in range(4):\n        if grid[r][c] == \"#\":\n            continue\n        if r == 0 and c == 0:\n            paths[r][c] = 1\n        else:\n            above = paths[r - 1][c] if r > 0 else 0\n            left = paths[r][c - 1] if c > 0 else 0\n            paths[r][c] = above + left\n    print(paths[r])\n","solution":"grid = [\"....\",\n        \"..#.\",\n        \"#...\"]\npaths = [[0] * 4 for _ in range(3)]\nfor r in range(3):\n    for c in range(4):\n        if grid[r][c] == \"#\":\n            continue\n        if r == 0 and c == 0:\n            paths[r][c] = 1\n        else:\n            above = paths[r - 1][c] if r > 0 else 0\n            left = paths[r][c - 1] if c > 0 else 0\n            paths[r][c] = above + left\n    print(paths[r])\n","hints":["The program prints each row once it's filled. The top row has no wall, so every cell in it has 1 path.","In the second row, the wall stays 0, and the cell after it gets only the paths from above: it can't be entered from the wall.","In the third row, the first cell is a wall. The cell next to it can then only be entered from above."],"tests":[{"name":"Output","stdin":"","expect":"[1, 1, 1, 1]\n[1, 2, 0, 1]\n[0, 2, 2, 3]","hidden":false}],"require":[],"forbid":[],"lines":["[1, 1, 1, 1]","[1, 2, 0, 1]","[0, 2, 2, 3]"],"more":[{"task":"Write `cheapest_path(cost)`. `cost` is a list of rows of numbers, all 0 or more, and the function returns the smallest total of the costs along a right-and-down path from the top-left cell to the bottom-right one, both included. The tests have grids of 300 × 300, so fill a table: trying every path takes forever.\n\nFor example, this program:\n\n```python\ncost = [[2, 7, 1, 3],\n        [1, 8, 2, 4],\n        [5, 1, 3, 1]]\nprint(cheapest_path(cost))\n```\n\nprints 13, the cost of the path through 2, 1, 5, 1, 3 and 1:\n\n```\n13\n```\n","kind":"code","seed":"def cheapest_path(cost):\n    pass\n\n\nif __name__ == \"__main__\":\n    cost = [[2, 7, 1, 3],\n            [1, 8, 2, 4],\n            [5, 1, 3, 1]]\n    print(cheapest_path(cost))\n","solution":"def cheapest_path(cost):\n    rows, cols = len(cost), len(cost[0])\n    best = [[0] * cols for _ in range(rows)]\n    for r in range(rows):\n        for c in range(cols):\n            if r == 0 and c == 0:\n                best[r][c] = cost[r][c]\n            elif r == 0:\n                best[r][c] = best[r][c - 1] + cost[r][c]\n            elif c == 0:\n                best[r][c] = best[r - 1][c] + cost[r][c]\n            else:\n                best[r][c] = min(best[r - 1][c], best[r][c - 1]) + cost[r][c]\n    return best[rows - 1][cols - 1]\n\n\nif __name__ == \"__main__\":\n    cost = [[2, 7, 1, 3],\n            [1, 8, 2, 4],\n            [5, 1, 3, 1]]\n    print(cheapest_path(cost))\n","hints":["Make a table `best` of the same size with `[[0] * cols for _ in range(rows)]`, where `best[r][c]` is the cheapest total of a path from the top-left cell to `(r, c)`.","The top-left entry is its own cost. On the top row, add the cost to the entry on the left; in the left column, to the entry above. Everywhere else, add it to the smaller of the two.","Go through the rows from the top and through each row from the left. The answer is the last entry of the last row."],"tests":[{"name":"Test 1","stdin":"","call":"cost = [[2, 7, 1, 3],\n        [1, 8, 2, 4],\n        [5, 1, 3, 1]]\nprint(cheapest_path(cost))\n","expect":"13","hidden":false},{"name":"cheapest_path([[9]])","stdin":"","call":"cheapest_path([[9]])\n","expect":"9","hidden":true,"returns":true},{"name":"cheapest_path([[4, 1, 6]])","stdin":"","call":"cheapest_path([[4, 1, 6]])\n","expect":"11","hidden":true,"returns":true},{"name":"cheapest_path([[3], [0], [7]])","stdin":"","call":"cheapest_path([[3], [0], [7]])\n","expect":"10","hidden":true,"returns":true},{"name":"cheapest_path([[1, 9, 1, 1], [1, 1, 1, 9], [9, 9, 1, 1]])","stdin":"","call":"cheapest_path([[1, 9, 1, 1], [1, 1, 1, 9], [9, 9, 1, 1]])\n","expect":"6","hidden":true,"returns":true},{"name":"cheapest_path([[0, 0], [0, 0]])","stdin":"","call":"cheapest_path([[0, 0], [0, 0]])\n","expect":"0","hidden":true,"returns":true},{"name":"cheapest_path([[1, 2, 9], [5, 9, 9], [1, 1, 1]])","stdin":"","call":"cheapest_path([[1, 2, 9], [5, 9, 9], [1, 1, 1]])\n","expect":"9","hidden":true,"returns":true},{"name":"cheapest_path([[(r * 31 + c * 17) % 10 for c in range(20)] for r in range(20)])","stdin":"","call":"cheapest_path([[(r * 31 + c * 17) % 10 for c in range(20)] for r in range(20)])\n","expect":"89","hidden":true,"big":true,"returns":true},{"name":"cheapest_path([[(r * 31 + c * 17) % 10 for c in range(300)] for r in range(300)])","stdin":"","call":"cheapest_path([[(r * 31 + c * 17) % 10 for c in range(300)] for r in range(300)])\n","expect":"1377","hidden":true,"big":true,"returns":true}],"require":[],"forbid":[]},{"task":"A park ranger's map is a list of strings where `#` is forest and `.` is open land. Write `largest_square(grid)`: how long is the side of the largest square patch of forest, a square in which every cell is `#`? A map without any forest gives 0.\n\nWork out, for every cell, `side[r][c]`: the side of the largest forest square that has the cell `(r, c)` as its **lower-right** corner. An open cell gets 0. A forest cell can stretch the squares that end just above it, just to its left and diagonally up-left of it, but only as far as the smallest of the three allows, so it gets that smallest entry plus 1 (a neighbor off the map counts as 0). The answer is the biggest entry. The tests have maps of 300 × 300, where checking every square one by one never ends.\n\nFor example, this program:\n\n```python\ngrid = [\"..##.\",\n        \"####.\",\n        \".###.\",\n        \".###.\",\n        \"#....\"]\nprint(largest_square(grid))\n```\n\nprints 3, for the square in the middle of the map:\n\n```\n3\n```\n","kind":"code","seed":"def largest_square(grid):\n    pass\n\n\nif __name__ == \"__main__\":\n    grid = [\"..##.\",\n            \"####.\",\n            \".###.\",\n            \".###.\",\n            \"#....\"]\n    print(largest_square(grid))\n","solution":"def largest_square(grid):\n    rows, cols = len(grid), len(grid[0])\n    side = [[0] * cols for _ in range(rows)]\n    biggest = 0\n    for r in range(rows):\n        for c in range(cols):\n            if grid[r][c] != \"#\":\n                continue\n            if r == 0 or c == 0:\n                side[r][c] = 1\n            else:\n                side[r][c] = 1 + min(side[r - 1][c], side[r][c - 1], side[r - 1][c - 1])\n            biggest = max(biggest, side[r][c])\n    return biggest\n\n\nif __name__ == \"__main__\":\n    grid = [\"..##.\",\n            \"####.\",\n            \".###.\",\n            \".###.\",\n            \"#....\"]\n    print(largest_square(grid))\n","hints":["Make a table `side` of zeros, the size of the map, and a variable for the biggest entry so far.","Fill it row by row, left to right. Skip `.` cells. A `#` on the top row or in the left column gets 1, since its square can't be bigger.","Any other `#` cell gets `1 + min(side[r - 1][c], side[r][c - 1], side[r - 1][c - 1])`. Keep the biggest entry, and return it."],"tests":[{"name":"Test 1","stdin":"","call":"grid = [\"..##.\",\n        \"####.\",\n        \".###.\",\n        \".###.\",\n        \"#....\"]\nprint(largest_square(grid))\n","expect":"3","hidden":false},{"name":"largest_square([\"....\", \"....\", \"....\"])","stdin":"","call":"largest_square([\"....\", \"....\", \"....\"])\n","expect":"0","hidden":true,"returns":true},{"name":"largest_square([\"#\"])","stdin":"","call":"largest_square([\"#\"])\n","expect":"1","hidden":true,"returns":true},{"name":"largest_square([\"###\", \"###\", \"###\"])","stdin":"","call":"largest_square([\"###\", \"###\", \"###\"])\n","expect":"3","hidden":true,"returns":true},{"name":"largest_square([\"##.\", \"###\", \".##\"])","stdin":"","call":"largest_square([\"##.\", \"###\", \".##\"])\n","expect":"2","hidden":true,"returns":true},{"name":"largest_square([\".####\", \"#####\", \"####.\", \"####.\"])","stdin":"","call":"largest_square([\".####\", \"#####\", \"####.\", \"####.\"])\n","expect":"3","hidden":true,"returns":true},{"name":"largest_square([\"#\" * 100 + \".\" + \"#\" * 199 for _ in range(300)])","stdin":"","call":"largest_square([\"#\" * 100 + \".\" + \"#\" * 199 for _ in range(300)])\n","expect":"199","hidden":true,"big":true,"returns":true},{"name":"largest_square([\"\".join(\".\" if (r * 7 + c * 3) % 101 == 0 else \"#\" for c in range(300)) for r in range(300)])","stdin":"","call":"largest_square([\"\".join(\".\" if (r * 7 + c * 3) % 101 == 0 else \"#\" for c in range(300)) for r in range(300)])\n","expect":"13","hidden":true,"big":true,"returns":true},{"name":"largest_square([(\".\" if r % 100 == 99 else \"#\") * 300 for r in range(300)])","stdin":"","call":"largest_square([(\".\" if r % 100 == 99 else \"#\") * 300 for r in range(300)])\n","expect":"99","hidden":true,"big":true,"returns":true}],"require":[],"forbid":[]}]}