動態規劃的三種解法,以POJ百煉1088滑雪為例

2021-09-07 19:22:26 字數 3635 閱讀 3593

看到這道題目,思路肯定是有的,用樣例輸入來說,一共25個結點的矩陣。只要求出從每個點開始往下滑的最長滑行長度。然後在這25個結果裡取最大值。就是題目的答案。然後如何求出乙個結點的最大滑行長度呢。不管是遞迴的思路還是遞推的思路,都要先寫出狀態轉移方程。那麼這道題的狀態轉移方程還是比較簡單的。乙個結點的最大滑行長度為:他上下左右四個結點裡,(比他高度低的那些結點的最大滑行長度+1.)的最大值。如果乙個結點的四周沒有比他高度低的結點,那麼他的最大滑行長度為1。這就是邊界值。知道這些之後,遞迴的**就非常好寫了。但是,簡單的遞迴可能會產生非常多的重複計算,例如,你在求解數字為25的結點的結果的時候,會遞迴得去求解一遍數字為18的結點的結果,而你求解數字為19的結點的時候也會去遞迴得求解數字為18的結點的結果。因為簡單的遞迴時間複雜度會很高,一定會超時的。這就是動態規劃要解決的問題。現在問題在於許多結點被重複計算了,所以導致時間浪費。那麼我們不妨設定乙個同樣規模的result二維陣列來儲存每乙個結點的結果。二維陣列裡每乙個值都初始化為-1(最大滑行長度一定是乙個正數,所以-1是不可能的),用-1來表示該結點的結果(下文中都把每個結點的最大滑行長度簡稱為結果)還未求出。然後在遞迴函式的最前面進行一次if判斷,如果結果已經求出,那麼直接返回結果,來阻止重複計算,達到良好的時間複雜度。接下來上**

#include #include #include #include using namespace std;

void getdata(vector> &height, const int r, const int c);

int memoryrecursion(vector> &height);//記憶型遞迴解法

int recursion(vector> &height, vector>&result, int i, int j);//遞迴主體

int allforonedp(vector> &height);//人人為我型遞推

int oneforalldp(vector> &height);//我為人人型遞推

int main()

int oneforalldp(vector> &height)//我為人人型遞推

; int r = height.size();

int c = height[0].size();

vectorascend(r*c);//遞增的表排序結點

for (int i = 0; i < r*c; ++i)//初始化表

sort(ascend.begin(), ascend.end(), [&height](const tablesort &a, const tablesort &b)->bool

);//用c++11的lambnd表示式進行快排表排序

/*開始我為人人型遞推*/

int maxlength = 0;

vector> result(height);

for (auto i = result.begin(); i != result.end(); ++i) }

/*我為人人,下標從0開始,因為每次迴圈的時候,是說明當前結點的最大長度已經求出,更新他附近的結點*/

for (size_t x = 0; x < ascend.size(); ++x)//按高度遞增的順序更新每個結點的最大滑行長度

if (ascend[x].j > 0 && height[ascend[x].i][ascend[x].j - 1] > high)//如果左邊的結點高度更高

if (ascend[x].i < r - 1 && height[ascend[x].i + 1][ascend[x].j] > high)//如果下面結點的高度更高

if (ascend[x].j < c - 1 && height[ascend[x].i][ascend[x].j + 1] > high)//如果右邊的結點高度更高

maxlength = max(maxlength, result[ascend[x].i][ascend[x].j]);//取整個滑雪道的最大值

} return maxlength;

}int allforonedp(vector> &height)//人人為我型遞推

; int r = height.size();

int c = height[0].size();

vectorascend(r*c);//遞增的表排序結點

for (int i = 0; i < r*c; ++i)//初始化表

sort(ascend.begin(), ascend.end(), [&height](const tablesort &a, const tablesort &b)->bool

);//用c++11的lambnd表示式進行快排表排序

/*開始人人為我遞推*/

int maxlength = 1;//要初始化為1,不能初始化為0,因為下面的x迴圈是從1開始的

/*如果初始化為0,那麼當只有乙個結點時,maxlength不會被更新*/

vector> result(height);

for (auto i = result.begin(); i != result.end(); ++i) }

for (size_t x = 1; x < ascend.size(); ++x)//按高度遞增的順序求解每個結點的最大滑行長度

if (ascend[x].j > 0 && height[ascend[x].i][ascend[x].j - 1] < high)//如果左邊的結點高度更低

if (ascend[x].i < r - 1 && height[ascend[x].i + 1][ascend[x].j] < high)//如果下面結點的高度更低

if (ascend[x].j < c - 1 && height[ascend[x].i][ascend[x].j + 1] < high)//如果右邊的結點高度更低

maxlength = max(maxlength, result[ascend[x].i][ascend[x].j]);//取整個滑雪道的最大值

} return maxlength;

}int memoryrecursion(vector> &height)//記憶型遞迴解法

} for (int i = 0; i < r; ++i)

}} return maxlength;

}int recursion(vector> &height, vector>&result, int i, int j)//從i,j出發的最大長度

if (j > 0 && height[i][j - 1] < height[i][j])//如果左邊結點的高度更低

if (i < static_cast(height.size() - 1) && height[i + 1][j] < height[i][j])//如果下面結點的高度更低

if (j < static_cast(height[0].size() - 1) && height[i][j + 1] < height[i][j])

if (result[i][j] == -1) //如果四周沒有比他低的點

} return result[i][j];

}void getdata(vector> &height, const int r, const int c)

}}

可以看到,遞迴的**還是非常簡單的。memoryrecursion函式就是動態規劃自頂向下的記憶型遞迴解法。

動態規劃三種寫法

leetcode 198 你是乙個專業的小偷,計畫偷竊沿街的房屋。每間房內都藏有一定的現金,影響你偷竊的唯一制約因素就是相鄰的房屋裝有相互連通的防盜系統,如果兩間相鄰的房屋在同一晚上被小偷闖入,系統會自動報警。給定乙個代表每個房屋存放金額的非負整數陣列,計算你在不觸動警報裝置的情況下,能夠偷竊到的最...

LetCode PlusOne的三種解法

原題目 given a number represented as an array of digits,plus one to the number.題目的意思是乙個用陣列表示的數字,在加一後仍然用這個陣列表示,加一後位數可能會增加,故最好用vector表示.class plusone priva...

逆元的三種解法

逆元 當求解公式 a b m 時,由於b a時,a b 0,所以需變除法為乘法 設c是b的逆元,則有b c 1 mod m 則 a b m a b 1 m a b bc m ac mod m 即a b的模等於a b的逆元的模 1.費馬小定理 在是素數的情況下,對任意整數都有。如果無法被整除,則有。可...