動態規劃演算法理解——從例題中學習
這些題目的難度是遞增順序來的,後續慢慢再補
第一題(牛妹的蛋糕)
題目描述
眾所周知,牛妹非常喜歡吃蛋糕。
第一天牛妹吃掉蛋糕總數三分之一(向下取整)多一個,第二天又將剩下的蛋糕吃掉三分之一(向下取整)多一個,以後每天吃掉前一天剩下的三分之一(向下取整)多一個,到第n天準備吃的時候只剩下一個蛋糕。
牛妹想知道第一天開始吃的時候蛋糕一共有多少呢?
示例1
輸入
複製
2
返回值
複製
3
思路:
1.(數學分析)首先假設第一天準備開始吃的時候有x份蛋糕,那麼第二天準備開始吃的時候的蛋糕 x-(x*1/3 + 1) = x * 2/3 - 1 份蛋糕。
2.(找狀態方程)設dp[i]為第i天準備開始吃的時候有dp[i]份蛋糕,那麼第二天準備開始吃的時候的蛋糕就為dp[i+1];根據1的分析可得 dp[i+1] = dp[i] * 2/3 - 1;
3.因為題目給定的n代表的是,最後一天她正準備吃的時候剩下的數量,我們要根據這個數量往前推出她第一天的蛋糕數。所以可得方程式dp[i] = 3 * (dp[i+1]+1) / 2; (就是2中的方程變形)。
完事寫程式碼
#include<iostream>
using namespace std;
int cakeNumber(int n) {
int dp = 1;
for(int i = n - 1; i >= 1; --i)
{
dp = 3 * (dp + 1) / 2;
}
return dp;
}
int main()
{
int n;
cin >> n;
cout << cakeNumber(n);
return 0;
}
第二題(牛妹的禮物)
題目描述
眾所周知,牛妹有很多很多粉絲,粉絲送了很多很多禮物給牛妹,牛妹的禮物擺滿了地板。
地板是N\times MN×M的格子,每個格子有且只有一個禮物,牛妹已知每個禮物的體積。
地板的座標是左上角(1,1) 右下角(N, M)。
牛妹只想要從屋子左上角走到右下角,每次走一步,每步只能向下走一步或者向右走一步或者向右下走一步
每次走過一個格子,拿起(並且必須拿上)這個格子上的禮物。
牛妹想知道,她能走到最後拿起的所有禮物體積最小和是多少?
思路
首先把題目給定的禮物體積存入一個二維陣列A[MAX][MAX]中,然後準備開始動態規劃了。設一個二維陣列dp[MAX][MAX],先初始化計算它的第一行第一列,因為第一行第一列的所有資料只能是通過它的前一個資料得來,初始完之後,計算其他的。
狀態方程 :dp[i][j] = min(dp[i-1][j-1],min(dp[i][j-1],dp[i-1][j])) + A[i][j];
就是判斷當前點的向上一個資料,向左的一個資料,左上的一個資料,這三個資料的最小值再加上當前的體積值就是該點的值。
完事寫程式碼
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
#define MAX 1100
int dp[MAX][MAX],A[MAX][MAX];
int main()
{
int m,n;
cin >> n >> m;
for(int i = 1; i <= n; ++i){
for(int j = 1; j <= m; ++j){
cin >> A[i][j];
dp[i][j] = 0;
}
}
dp[1][1] = A[1][1];
for(int i = 2; i <= n; ++i){
dp[i][1] = dp[i-1][1] + A[i][1];
}
for(int i = 2; i <= m; ++i){
dp[1][i] = dp[1][i-1] + A[1][i];
}
for(int i = 2; i <= n; ++i){
for(int j = 2; j <= m; ++j){
dp[i][j] = min(dp[i-1][j-1],min(dp[i-1][j],dp[i][j-1])) + A[i][j];
}
}
cout << dp[n][m];
return 0;
}
第三題(變相)
題目描述
牛牛準備在一個3行n列的跑道上跑步。一開始牛牛可以自己選擇位於(1,1)還是(2,1)還是(3,1)。
跑道的每一格都有一些金幣,當牛牛跑到一個格子,他會獲得這個格子的所有金幣。
當牛牛位於第i行第j列時,他可以的下一步最多可能有三種選擇:
- 不花費金幣跑到第i行第j+1列
- 花費m_jm
j
的金幣跑到第i-1行第j+1列(如果i=1則不可以這麼跑)。 - 花費m_jm
j
的金幣跑到第i+1行第j+1列(如果i=3則不可以這麼跑)。
(牛牛是一個富豪,本身帶了很多的金幣,所以你不用擔心他錢不夠用)
現在告訴你所有格子的金幣數量和每一列的金幣花費,牛牛想知道他跑到第n列最多可以賺得多少金幣(賺得金幣=獲得金幣-消耗金幣)
思路
首先用一個二維陣列A[3][MAX]把每個格子的金幣數儲存起來,然後就是初始化dp[][]陣列了。 這個題目的初始化只能初始化dp的第一列,和上一題不同,這個題後面的數都是可以變化的。這題的遍歷只能一列一列的遍歷,不能一行行的來(畫個圖,拿資料推一遍你就知道了。)
狀態方程:
i == 0:(就是比較一下該點左邊和它左下減去跳行的金幣後的大小)
dp[i][j] = max(dp[i][j-1],dp[i+1][j-1] - m[j-1]) + A[i][j];
i == 1:(比較左邊,左上左下減去跳行的金幣後的大小)
dp[i][j] = max(dp[i][j-1],max(dp[i-1][j-1],dp[i+1][j-1])-m[j-1]) + A[i][j];
i == 2:(比較左邊,左上減去跳行的金幣後的大小)
dp[i][j] = max(dp[i][j-1],dp[i-1][j-1] - m[j-1]) + A[i][j];
測試資料
3
1 9 3
6 4 6
1 1 5
3 2 1
#include<bits/stdc++.h>
using namespace std;
#define MAX 1100
int n;
int A[3][MAX],dp[3][MAX],m[MAX];
int main()
{
cin >> n;
for(int i = 0; i < 3; ++i){
for(int j = 0; j < n; ++j){
cin >> A[i][j];
}
}
for(int i = 0; i < n; ++i){
cin >> m[i];
}
dp[0][0] = A[0][0]; dp[1][0] = A[1][0]; dp[2][0] = A[2][0];
for(int j = 1; j < n; ++j){
for(int i = 0; i < 3; ++i){
if(i == 1){
dp[i][j] = max(dp[i][j-1],max(dp[i-1][j-1],dp[i+1][j-1])-m[j-1]) + A[i][j];
}
else if(i == 0){
dp[i][j] = max(dp[i][j-1],dp[i+1][j-1] - m[j-1]) + A[i][j];
}
else{
dp[i][j] = max(dp[i][j-1],dp[i-1][j-1] - m[j-1]) + A[i][j];
}
}
}
int t = -100;
for(int i = 0; i < 3; ++i){
if(dp[i][n-1] > t) t = dp[i][n-1];
}
cout << t << endl;
return 0;
}
第四題(簡單變相)
題目描述
牛牛準備在一個3行n列的跑道上跑步。一開始牛牛位於(1,1)。
當牛牛位於第i行第j列時,他可以的下一步最多可能有三種選擇:
- 跑到第i行第j+1列
- 跑到第i-1行第j+1列(如果i=1則不可以這麼跑)。
- 跑到第i+1行第j+1列(如果i=3則不可以這麼跑)。
跑道上有一些格子設定了路障(一個格子可能有多個路障),牛牛不能跑到路障上。現在牛牛想知道,從(1,1)到(3,n)有多少條不同的路徑?
為了防止答案過大,答案對1e9+7取模。
思路
跟第三題差不多,dp[i][j] 代表的是該格子有幾條路可到達。(牛客上是提交核心程式碼)
const int mod = 1e9+7;
class Solution {
public:
int solve(int n, int m, vector<int>& x, vector<int>& y) {
long long dp[4][n];
memset(dp,0,sizeof(dp));
for(int i = 1; i <= m; ++i){
dp[x[i - 1]][y[i - 1]] = -1;
}
dp[1][1] = 1; dp[2][1] = 0; dp[3][1] = 0;
for(int i = 2; i <= n; ++i){
dp[1][i] = dp[1][i] == -1 ? dp[1][i] = 0 : dp[1][i] = (dp[i][j-1]+dp[i+1][j-1]) % mod;
dp[2][i] = dp[2][i] == -1 ? dp[2][i] = 0 : dp[1][i] = (dp[i][j-1]+dp[i-1][j-1]+dp[i+1][j-1]) % mod;
dp[3][i] = dp[3][i] == -1 ? dp[3][i] = 0 : dp[1][i] = (dp[i][j-1]+dp[i-1][j-1]) % mod;
}
return dp[3][n];
}
};
相關文章
- 動態規劃演算法(DP)學習<1>動態規劃演算法
- 01揹包問題理解動態規劃演算法動態規劃演算法
- 動態規劃練習題動態規劃
- 動手學強化學習(四):動態規劃演算法強化學習動態規劃演算法
- 動態規劃學習筆記動態規劃筆記
- 基於動態規劃的強化學習演算法動態規劃強化學習演算法
- 動態規劃演算法動態規劃演算法
- 演算法_動態規劃演算法動態規劃
- 演算法-動態規劃演算法動態規劃
- 以最長公共子序列問題理解動態規劃演算法(DP)動態規劃演算法
- 演算法系列-動態規劃(1):初識動態規劃演算法動態規劃
- 好題——動態規劃動態規劃
- 動態規劃專題動態規劃
- 動態規劃題單動態規劃
- 前端學習演算法1 :老虎和羊,吃不吃問題(動態規劃入門)前端演算法動態規劃
- 大廠常考動態規劃演算法題動態規劃演算法
- 最小總和問題(動態規劃演算法)動態規劃演算法
- “插花問題”的動態規劃法演算法動態規劃演算法
- 插花問題的“動態規劃法”演算法動態規劃演算法
- 一維動態規劃和二維動態規劃中兩道經典題目動態規劃
- 前端演算法 - 動態規劃前端演算法動態規劃
- 動態規劃演算法(轉)動態規劃演算法
- 動態規劃---例題3.最大子段和問題動態規劃
- LeetCode 動態規劃 House Robber 習題LeetCode動態規劃
- 動態規劃練習 6動態規劃
- 動態規劃的理解與案例分析動態規劃
- 動態規劃解題方法動態規劃
- 動態規劃做題思路動態規劃
- Leetcode 題解演算法之動態規劃LeetCode演算法動態規劃
- 【每日演算法】動態規劃四演算法動態規劃
- 初級演算法-動態規劃演算法動態規劃
- Python演算法:動態規劃Python演算法動態規劃
- 【動態規劃(一)】動態規劃基礎動態規劃
- 強化學習(三)用動態規劃(DP)求解強化學習動態規劃
- 整數劃分問題(動態規劃)動態規劃
- 力扣練習-動態規劃力扣動態規劃
- 從“股票問題”談動態規劃問題的解決思路動態規劃
- 動態規劃 擺花 題解動態規劃