DP系列:
https://blog.csdn.net/nameofcsdn/article/details/106417240
因爲最近在寫DP總結系列,所以選了一個二維DP的題目,把各種代碼都寫了一遍,並在此進一步探索。
力扣OJ 63. 不同路徑 II (二維DP)https://blog.csdn.net/nameofcsdn/article/details/106537960
這裏面的4個代碼,涉及到遞歸和非遞歸、非遞歸的不同順序、空間壓縮。
第一個問題來了,非遞歸寫法有沒有空間壓縮寫法呢?
以題目中的數據和我的遞歸算法爲例
輸入:
[
[0,0,0],
[0,1,0],
[0,0,0]
]
int dp(vector<vector<int>>& obs,int x,int y)
{
if(x<0||x>=obs.size()||y<0||y>=obs[0].size()||obs[x][y])return 0;
if(ans[x][y])return ans[x][y];
return ans[x][y]=dp(obs,x,y-1)+dp(obs,x-1,y);
}
首先看看遞歸寫法的調用過程:
可以發現,這棵樹其實是一個斜着的矩形,而這個矩形和輸入的矩形obs是對應的。
然後,我們看看dp函數實際被調用的過程:
class Solution {
public:
vector<vector<int>>ans;
int dp(vector<vector<int>>& obs,int x,int y)
{
cout<<x<<" "<<y<<" ";
if(x<0||x>=obs.size()||y<0||y>=obs[0].size()||obs[x][y])return 0;
if(ans[x][y])return ans[x][y];
return ans[x][y]=dp(obs,x,y-1)+dp(obs,x-1,y);
}
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
ans=obstacleGrid;
for(int i=0;i<ans.size();i++)for(int j=0;j<ans[0].size();j++)ans[i][j]=0;
ans[0][0]=1;
return dp(obstacleGrid,obstacleGrid.size()-1,obstacleGrid[0].size()-1);
}
};
int main()
{
vector<int>v1;
v1.push_back(0);
v1.push_back(0);
v1.push_back(0);
vector<vector<int>>obs;
obs.push_back(v1);
obs.push_back(v1);
obs.push_back(v1);
obs[1][1]=1;
Solution s;
cout<<s.uniquePathsWithObstacles(obs);
return 0;
}
輸出:
2 2 2 1 2 0 2 -1 1 0 1 -1 0 0 1 1 1 2 1 1 0 2 0 1 0 0 -1 1 -1 2 2
把-1去掉,得到:
2 2 2 1 2 0 1 0 0 0 1 1 1 2 1 1 0 2 0 1 0 0
用同樣的方法,如果輸入的是
[
[0,0,0],
[0,1,0],
[0,0,0]
]
那麼遞歸的實際執行過程就是
2 2 2 1 2 0 2 -1 1 0 1 -1 0 0 1 1 1 0 0 1 0 0 -1 1 1 2 1 1 0 2 0 1 -1 2 6
把-1去掉,得到:
2 2 2 1 2 0 1 0 0 0 1 1 1 0 0 1 0 0 1 2 1 1 0 2 0 1
不難看出來,這就是DFS
總結:
1,遞歸程序都是基於棧的。
2,DFS是基於棧的,BFS是基於隊列的。
3,動態規劃的問題,解空間都可以映射爲樹。
4,動態規劃的遞歸程序,是對解空間的DFS。