文章目录
- 62. 不同路径
- 题意
- 解法1 排列组合
- 解法2 动态规划
- 64. 最小路径和
- 题意
- 解法1 DFS(剪枝也超时)
- 解法2 动态规划
62. 不同路径
题意
- 一道数学题,排列组合/小学奥赛题。
- 动态规划不是一般来解决最值问题的吗,这道题为什么会想到dp?
解法1 排列组合
从左上角到右下角,一共要走m+n-2步,其中向右n-1步,向下m-1步,因此路径的总数,相当于从m+n-2中选择m-1个向下的步数,即排列组合。
- 但是,需要注意的是,题目只保证最后结果在
int
型范围内,而实际上如果按下面的代码运行,即便中间运算已经用long long
存储,还是会溢出,所以需要一边乘一边除(即便是一边乘一边除,中间过程也必须用long long
,否则中间计算会超出int型可表示范围)。
class Solution {
public:
int uniquePaths(int m, int n) {
long long ans=1;
for(int i=n;i<=m+n-2;i++)
ans=ans*i; //会溢出
for(int i=1;i<m;i++)
ans/=i;
return ans;
}
};
// ac代码
class Solution {
public:
int uniquePaths(int m, int n) {
long long ans=1;
for(int i=n,j=1;i<=m+n-2;i++,j++)
ans=ans*i/j;
return ans;
}
};
解法2 动态规划
dp[i][j]
表示走到 (i,j) 这个位置有几种走法。dp[i][j]=dp[i-1][j]+dp[i][j-1]
。- 注意
dp[0][0]
和边界情况(i-1
和j-1
)处理(也可以将dp[0,:]
和dp[:,0]
全置1)。
class Solution {
public:
int uniquePaths(int m, int n) {
vector<vector<int> > dp(m,vector<int>(n,0));
dp[0][0]=1;
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(dp[i][j]==0) //为了维护dp[0][0]
{
int left=j==0?0:dp[i][j-1];
int top=i==0?0:dp[i-1][j];
dp[i][j]=left+top;
}
}
}
return dp[m-1][n-1];
}
};
Attention
- 二维数组的定义
vector<vector<int>> asd1(row, vector<int>(column, 0)); //初始化row*column二维动态数组,初始化值为0
- 动态规划解法中,其实只需要保存
dp[i-1][j]
和dp[i][j-1]
两个数,还有空间优化的余地。 - 排列组合基础
64. 最小路径和
题意
- 只能向右或向下走,约束很强,所以很好遍历!
- 求最小值。
- 和第62题有相似之处。
解法1 DFS(剪枝也超时)
建立一个队列q和一个数组value(记录每个点的最小值),将(0,0)压入队列,然后每从队列中取出一个点,就将其右和下两个点压入队列中,同时更新其右和下两个点的最小值。
但是由于超时,需要剪枝。所以只有当前点使得其右或下的点的最小值被更新时,才将这个点压入队列中。
但是依旧超时,,,
class Solution {
public:
int minPathSum(vector<vector<int>>& grid) {
// m行n列
int m=grid.size(),n=grid[0].size();
vector<vector<int>> value(m,vector<int>(n,-1));
queue<pair<int,int> > q;
pair<int,int> st(0,0);
q.push(st);
value[0][0]=grid[0][0];
while(!q.empty())
{
pair<int,int> tmp=q.front();
q.pop();
int x=tmp.first,y=tmp.second;
if(x+1<m&&y<n)
{
int tmp_value=value[x][y]+grid[x+1][y];
if(value[x+1][y]!=-1)
{
if(tmp_value<=value[x+1][y])
{
value[x+1][y]=tmp_value;
pair<int,int> nxt(x+1,y);
q.push(nxt);
}
}
else
{
value[x+1][y]=tmp_value;
pair<int,int> nxt(x+1,y);
q.push(nxt);
}
}
if(y+1<n&&x<m)
{
int tmp_value=value[x][y]+grid[x][y+1];
if(value[x][y+1]!=-1)
{
if(tmp_value<=value[x][y+1])
{
value[x][y+1]=tmp_value;
pair<int,int> nxt(x,y+1);
q.push(nxt);
}
}
else
{
value[x][y+1]=tmp_value;
pair<int,int> nxt(x,y+1);
q.push(nxt);
}
}
}
return value[m-1][n-1];
}
};
解法2 动态规划
和第62题不同的处理是,这里左边界和上边界的点要单独处理。最左列的点只能从它上面的点过来,而最上行的点只能从它左边过来。
其他没什么难点。
class Solution {
public:
int minPathSum(vector<vector<int>>& grid) {
// m行n列
int m=grid.size(),n=grid[0].size();
vector<vector<int>> dp(m,vector<int>(n,-1));
dp[0][0]=grid[0][0];
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(i==0&&j!=0)
dp[i][j]=dp[i][j-1]+grid[i][j];
else if(j==0&&i!=0)
dp[i][j]=dp[i-1][j]+grid[i][j];
else
{
int left=j-1>=0?dp[i][j-1]:0;
int top=i-1>=0?dp[i-1][j]:0;
dp[i][j]=min(left+grid[i][j],top+grid[i][j]);
}
}
}
return dp[m-1][n-1];
}
};
ATTENTION
- pair的用法: