本文記錄自己的學習心得和一些要點,牆裂推薦參考此連接進行學習
一、從斐波那契入手
記憶搜索算法實現斐波那契
int helper(vector<int>& memo, int n){
if (n == 1 || n == 2)return 1;
if (memo[n] != 0) return memo[n];//只要不是0就會記錄下來
memo[n] = helper(memo, n - 1) + helper(memo, n - 2);
return memo[n];
}
int fib1(int n){
if (n < 1) return 0;//n = 0的情況
vector<int> meno(n + 1, 0);
int res = helper(meno, n);
return res;
}
自底向上實現斐波那契數列
int fib3(int n){
vector<int>dp(n+1,0);
dp[1] = dp[2] = 1;
for (int i = 3; i <= n; ++i){
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
優化一下空間,之和兩個狀態相關,可以使用兩個變量替代
int fib4(int n){
if (n == 1 || n == 2) return 1;
int sum = 0;
int pre = 1;//初始狀態
int cur = 1;//初始狀態
for (int i = 3; i <= n; ++i){
sum = pre + cur;
cur = sum;//狀態轉移
pre = cur;//狀態轉移
}
return cur;
}
二、湊零錢問題
主要找到狀態轉移方程和初始條件就可以了,狀態轉移方程一般時按照題目思路寫幾個,寫着寫着歸納法就出來了,初始條件也一樣。寫完之後就可以按照自底向上的方法法寫出代碼了。
有了狀態轉移方程和初始條件就可以寫了.
int combinechange(vector<int>&coins,int n){
if (n < 0) return -1;
vector<int> dp(n + 1, 0);
dp[0] = 0;
for (int i = 1; i <= n; ++i){//計算所有值
int res = INT_MAX;
for (int j = 0; j < coins.size(); ++j){//遍歷coins,計算每一個值都需要所有的coins
if (i>=coins[j]){//滿足i>coins[j],否則無解
res = min(res, 1 + dp[i - coins[j]]);
}
else break;
}
dp[i] = res;
}
for (int i = 0; i < dp.size(); ++i){
cout << dp[i] << endl;
}
return dp[n];
}
修改一下dp數組初始條件得到更簡潔的代碼:
int combinechange1(vector<int>&coins, int n){
if (n < 0) return -1;
vector<int> dp(n + 1, n);//dp初始化爲n,最好選擇小的更新即可
dp[0] = 0;
for (int i = 1; i <= n; ++i){
for (int j = 0; j < coins.size(); ++j){
if (i >= coins[j]){
dp[i] = min(dp[i], 1 + dp[i - coins[j]]);
}
else break;
}
}
for (int i = 0; i < dp.size(); ++i){
cout << dp[i] << endl;
}
return dp[n];
}
這裏的狀態方程中前一個狀態爲dp[i-coins[j]],故不像斐波那契那樣只和兩個狀態有關,這裏的dp數組就無法像斐波那契那樣直使用兩個變量去更新了。
三、最長遞增子序列問題(力扣300)
int lengthOfLIS(vector<int>& nums) {
if(nums.size()==0) return 0;
vector<int> dp(nums.size(),1);
dp[0]=1;
for(int i = 1;i<nums.size();++i){
for(int j = 0;j<i;++j){
if(nums[i]>nums[j]){
dp[i] = max(dp[i],1+dp[j]);
}
}
}
int res = 0;
for(int i = 0;i<dp.size();++i){
res = max(res,dp[i]);
}
return res;
}
四、跳躍到數組最後一個元素需要最少的步數(力扣45)
趁熱打鐵,這個題和上一題套路都是一樣的
數組1:[2,3,1,1,4] 從2跳到3再跳到最後,需要兩步
數組2:[3,2,1,0,4] 無法跳到組後一個元素,返回-1
int jump(vector<int>& nums) {
if(nums.size()==0) return 0;
vector<int>dp(nums.size(),INT_MAX);
dp[0]=0;
for(int i = 1;i<nums.size();++i){
for(int j = 0;j<i;++j){
if(nums[j]>=(i-j)){
dp[i] = min(dp[i],1+dp[j]);
}
}
}
return dp[dp.size()-1];
}
這個題考的應該是貪心,用動態規劃做並不是最優的,對於全1也通不過。。。先不管了不影響我們理解動態規劃。
五、最大子序和
繼續趁熱打鐵做一下最大子序和,一樣的套路
int maxSubArray(vector<int>& nums) {
vector<int> dp(nums.size(),INT_MIN);
dp[0] = nums[0];
for(int i = 1;i<nums.size();++i){
dp[i] = max(nums[i],nums[i]+dp[i-1]);
}
int res = dp[0];
for(int i=0;i<dp.size();++i){
res = max(res,dp[i]);
}
return res;
}