很久之前接觸過這樣一道題目,總共有十層階梯,從1層開始往上爬,每次可以上1層或者2層,問到10層總共有多少種方法?
思路:這個問題就是動態規劃的一個經典例子,所謂動態規劃,就是把複雜的問題進行拆解,拆解成一個個子問題,而這類問題最後非常適合使用遞歸來解決。諸如這道題目,可以記到某層階梯的走法爲F(n),那麼到10層階梯就是F(10)。 那麼F(10)等於什麼呢,這裏進行假設,如果只差最後一次就可以走到10層,那麼此時有幾種情況呢?
- 在9層,往上走1步即可。
- 在8層,往上走2步即可。
那麼此時可以得出F(10)=F(9)+F(8); — 最優子結構
其實這裏就可以得出F(n)=F(n-1) + F(n-2) — 狀態轉移方程
而當n=2時,也就是走上2層,馬上可以知道只有2種方法(0->2,0->1->2)
也就是F(2)=2,同理F(1)=1. — 邊界情況
寫成代碼也很容易,這裏就不寫了。
然後這次還遇到了一個類似的題目,源自力扣
鏈接
其實大致的思路還是一樣的,只是增加了對二維數組中,某一個數字的判斷邏輯,剛剛開始我是這樣做的
/**
* @param {number[][]} obstacleGrid
* @return {number}
*/
var uniquePathsWithObstacles = function(obstacleGrid) {
// 座標軸向下爲x軸,向右爲y軸
// obstacleGrid的數組長度爲n,表示矩陣高度
// 其中每個元素的長度都是m,表示矩陣寬度
var map = new Map();
var F = function (x,y){
// x,y 分別爲網格的縱座標.橫座標
/* 0=<x<=n-1 */
/* 0=<y<=m-1 */
if (x ===0 && y === 0) {
return obstacleGrid[x][y]==0 ? 1:0;
}
if (obstacleGrid[x][y] === 1){
return 0;
} else {
if (x === 0) {
return F(x,y-1);
} else if (y === 0) {
return F(x-1,y);
} else {
return F(x-1,y) + F(x,y-1);
}
}
}
return F(obstacleGrid.length - 1,obstacleGrid[0].length - 1);
};
但是某一個測試用例卻顯示超時了,所以這裏得做一些額外的處理,我的思路是對某些計算的結果做一下緩存,每一次執行F(n)時先去Map裏面取,如果取不到再進行遞歸運算,並緩存下來。改動後的代碼如下
/**
* @param {number[][]} obstacleGrid
* @return {number}
*/
var uniquePathsWithObstacles = function(obstacleGrid) {
// 座標軸向下爲x軸,向右爲y軸
// obstacleGrid的數組長度爲n,表示矩陣高度
// 其中每個元素的長度都是m,表示矩陣寬度
var map = new Map();
var cacheCalc = function (x,y) {
if (map.has([x,y].join(','))) return map.get([x,y].join(','));
else {
var cacheValue = F(x,y);
map.set([x,y].join(','),cacheValue);
return cacheValue;
}
}
var F = function (x,y){
// x,y 分別爲網格的縱座標.橫座標
/* 0=<x<=n-1 */
/* 0=<y<=m-1 */
if (x ===0 && y === 0) {
return obstacleGrid[x][y]==0 ? 1:0;
}
if (obstacleGrid[x][y] === 1){
return 0;
} else {
if (x === 0) {
return cacheCalc(x,y-1);
} else if (y === 0) {
return cacheCalc(x-1,y);
} else {
return cacheCalc(x-1,y) + cacheCalc(x,y-1);
}
}
}
return F(obstacleGrid.length - 1,obstacleGrid[0].length - 1);
};