我第一时间想到的方案是化归而不是规划,毕竟之前的问题类似,所以就有了这个版本:
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) { if (obstacleGrid.empty()){ return 0; } const int begin = obstacleGrid.front().front(); if (begin == 1){ return 0; } const size_t m = obstacleGrid.size(); const size_t n = obstacleGrid.front().size(); vector<vector<int>> record((m), vector<int>(n)); function<int(size_t, size_t)> getPathsNum; getPathsNum = [&](int a, int b) { if (obstacleGrid[a][b] == 1){ return 0; } if (a == 0 && b == 0){ return 1; } int&& result = 0; if (a == 0){ result = getPathsNum(0, b - 1); record[a][b] = result; return result; } if (b == 0){ result = getPathsNum(a - 1, 0); record[a][b] = result; return result; } const int paths_num = record[a][b]; if (paths_num != 0){ return paths_num; } result = getPathsNum(a - 1, b) + getPathsNum(a, b - 1); record[a][b] = result; return result; }; return getPathsNum(m - 1, n - 1); }
感觉:嗯嗯,可以可以。一测时间, 92 ms。尼玛用 ruby 的都比我速度快,这玩毛。
后来发现我又陷入 DP 就是递归的思维定势里了,f(m, n) = f(m - 1, n) + f(m, n -1) 这个式子固然可以反着推,但正着填充不是更好?这才发现上面的版本根本不是用的动态规划的思想。
真正 DP 的思想是填充。当然填充也很简单大概意思就是 result[m, n] = result[m - 1, n] + result[m, n - 1]。这才是动规的思想好不拉!
能更省空间吗?我发现其实每一行的结果只依赖上一行,再往上的对它并没有直接影响。那还存储它们作甚?所以只需要一行数据的空间,然后逐行扫描。
int uniquePathsWithObstacles(vector<vector<int> > &obstacleGrid) { if (obstacleGrid.empty() || obstacleGrid[0][0] == 1 || obstacleGrid.back().back() == 1){ return 0; } const size_t row_length = obstacleGrid[0].size(); vector<int> row(row_length); auto const& first_row = obstacleGrid[0]; bool is_blocked = false; for (size_t i = 0; i != row_length; ++i){ if (is_blocked || first_row[i] == 1){ row[i] = 0; is_blocked = true; }else{ row[i] = 1; } } for (size_t i = 1; i != obstacleGrid.size(); ++i){ for (size_t j = 0; j != row_length; ++j){ if (obstacleGrid[i][j] == 1){ row[j] = 0; } else if (j > 0){ row[j] = row[j] + row[j - 1]; } } } return row.back(); }
时间: 2024-10-24 15:37:37