動態(tài)規(guī)劃:
9-12
斐波那契數(shù)列
對重復計算,進行優(yōu)化,進行記憶化搜索
假設基本的問題已經(jīng)被解決,依次內(nèi)推。
動態(tài)規(guī)劃:將原問題拆解成若干個子問題,同時保存子問題的答案,使得每個子問題只求解一次,最終獲得原問題的答案。
leetcode 20
自頂向下分析可得:
f(n)=f(n-1)+f(n-2)
將上述函數(shù)修改動態(tài)規(guī)劃寫法:
部分代碼如下:
在這里插入代碼片
//跳臺階
#include<iostream>
#include<vector>
using namespace std;class Solution{private:vector<int> memo;int calcWays(int n){if(n==0 || n==1)return 1;//if(n==1)//return 1;//if(n==2)//return 2;if(memo[n]==-1)memo[n]=calcWays(n-1)+calcWays(n-2);retrun memo[n]; }public:int climbStairs(int n){memo=vector<int>(n+1,-1);return calcWays(n);}
};int main(){return 0;
}//調(diào)臺階修改成動態(tài)規(guī)劃
#include<iostream>
#include<vector>
using namespace std;class Solution{public:int climbStairs(int n){vector<int> memo(n+1,-1);memo[0]=1;memo[1]=1;for(int i=2;i<n;i++)memo[i]=memo[i-1]+memo[i-2];return memo[n];}
};
leetcode:120
leetcode 64
每一步只能左移或者下移
9-3
leetcode 343整數(shù)分割
最優(yōu)子結(jié)構(gòu): 通過求子問題的最優(yōu)解,可以獲得原問題的最優(yōu)解
部分代碼如下:
在這里插入代碼片
//整數(shù)分割的問題
#include<iostream>
#include<cassert>
#include<vector>
//記憶化搜索的關(guān)鍵是對訪問過的值進行記錄,即用memo數(shù)組對結(jié)果進行保存 using namespace std;class Solution{private:int max3(int a,int b,int c){return max(a,max(b,c));}//將n進行分割,至少分割成兩部分,可以獲得的最大乘積 int breakInteger(int n){if(n==1)return 1;if(memo[n]!=-1)return memo[n];int res=-1;for(int i=1; i<=n-1; i++)//i+(n-i)res=max3(res,i*(n-i),i*breakInteger(n-i));memo[n]=res;return res; }
public:int integerBreak(int n){assert(n>=2); memo=vector<int>(n+1,-1);return breakInteger(n);}
};int main() {return 0;
}
修改成動態(tài)規(guī)劃的問題
部分代碼如下:
//整數(shù)分割的問題
#include<iostream>
#include<cassert>
#include<vector>
//記憶化搜索的關(guān)鍵是對訪問過的值進行記錄,即用memo數(shù)組對結(jié)果進行保存 using namespace std;class Solution{private:int max3(int a,int b,int c){return max(a,max(b,c));}}
public:int integerBreak(int n){assert(n>=2); //將memo[i]進行分割,至少分割成兩部分,可以獲得的最大乘積 vector<int> memo(n+1,-1);memo[1]=1;for(int i=1;i<n-1;i++)//求解memo[i]for(int j=1;j<=i-1;j++)//j+(i-j)memo[i]=max3(memo[i],j*(i-j),j*memo[i-j]);return memo(n);}
};int main() {return 0;
}
leetcode 279完全平方數(shù)
leetcode 91:解析方式
leetcode 62 不同路徑
leetcode 63 機器人 障礙物
9-4
leetcode 198 打家劫舍
房子不相鄰
價值最大
在這里插入代碼片//打家劫舍
#include<iostream>
#include<vector>using namespace std;class Solution{private://memo[i]表示搶劫nums[i...n]所能獲得的最大收益vector<int> memo; //考慮搶劫nums[index...nums.size())這個范圍的所有房子 int tryRob(vector<int> &nums,int index){if(index>=nums.size())return 0;if(memo[index]!=-1)return memo[index];int res=0; for(i=index;i<nums.size();i++)res=max(res,nums[i]+tryRob(nums,i+2); memo[index]=res;return res; }public:int rob(vector<int>& nums){memo=vector<int>(nums.size(),-1);return tryRob(nums,0);}}; int main(){return 0;}
修改成動態(tài)規(guī)劃問題
在這里插入代碼片
//打家劫舍動態(tài)規(guī)劃
#include<iostream>
#include<vector>using namespace std;class Solution{private:public:int rob(vector<int>& nums){int n=nums.size();if(n==0)return 0;//memo[i]表示搶劫nums[i...n-1]所能獲得的最大收益vector<int>memo(n,-1);memo[n-1]=nums[n-1];for(int i=n-2;i>=0;i--)//memo[i]for(int j=i; j<n;j++)memo[i]=max(memo[i],nums[j]+(j+2<n ? memo[j+2] : 0));return memo[0];} }; int main(){return 0;}
leetcode 213
leetcode 337
leetcode 309
設計一個自動交易算法
9-5 0-1背包問題
貪心算法?優(yōu)先放入平均價值最高的物品?
最優(yōu)解應該為22>16貪心算法
在這里插入代碼片
//0-1背包問題
#include<iostream>
#include<vector>using namespace std;class knapsack01{
private:vector<vector<int>> memo;//用[0...index]的物品,填充容積為c的背包的最大價值 int bestValue(const vector<int> &w, const vector<int> v, int index, int c){if(index<0 || c<=0)return 0;if(memo[index][c]!=-1)return memo[index][c];int res=bestValue(w,v,index-1,c);if(c>=w[index])res=max(v[index]+bestValue(w,v,index-1,c-w[index]); memo[index][c]=res;return res;}public:int knapsack01(const vector<int> &w, const vector<int> &v, int C){int n=w.size();memo=vector<vector<int>>(n,vector<int>(C+1,-1));return bestValve(w,v,n-1,c); } }; int main(){return 0;}
如何自頂向下解決該問題
橫軸為容量,縱軸為背包id
在這里插入代碼片
//0-1背包動態(tài)規(guī)劃問題
#include<iostream>
#include<vector>using namespace std;class knapsack01{public:int knapsack01(const vector<int> &w, const vector<int> &v, int C){assert(w.size()==v.size()); int n=w.size();if(n==0 || C==0)return 0;vector<vector<int>> memo(n,vector<int>(C+1,-1));for(int j=0; j<=C; j++)memo[0][j]=(j>=w[0] ? v[0] : 0);for(int i=1; i<n; i++)for(int j=0; j<=C; j++){//0-i這些物品,背包為j memo[i][j]=memo[i-1][j];if(j>=w[i])memo[i][j]=max(memo[i][j],v[i]+memo[i-1][j-w[i]]) }return memo[n-1][C]; } }; int main(){return 0;}
9-6
0-1背包問題的優(yōu)化
在這里插入代碼片
//0-1背包動態(tài)空間復雜度的優(yōu)化問題
#include<iostream>
#include<vector>using namespace std;class knapsack01{public:int knapsack01(const vector<int> &w, const vector<int> &v, int C){assert(w.size()==v.size()); int n=w.size();if(n==0 || C==0)return 0;vector<vector<int>> memo(2,vector<int>(C+1,-1));//將n改為2for(int j=0; j<=C; j++)memo[0][j]=(j>=w[0] ? v[0] : 0);for(int i=1; i<n; i++)for(int j=0; j<=C; j++){//0-i這些物品,背包為j memo[i%2][j]=memo[(i-1)%2][j];//對2取模,優(yōu)化空間。從n*c的復雜度變成了2*c的復雜度 if(j>=w[i])memo[i%2][j]=max(memo[i][j],v[i]+memo[(i-1)%2][j-w[i]]) }return memo[(i-1)%2][C]; } }; int main(){return 0;}
只考慮0的情況狀態(tài)
如果現(xiàn)在將1納入背包的狀態(tài),從右向左更新狀態(tài),比如5,在3的基礎上加1的為16,大于6則更新為16.依次類推,更新所有狀態(tài)。
在這里插入代碼片
//0-1背包動態(tài)空間復雜度的繼續(xù)優(yōu)化問題
#include<iostream>
#include<vector>using namespace std;class knapsack01{public:int knapsack01(const vector<int> &w, const vector<int> &v, int C){assert(w.size()==v.size()); int n=w.size();if(n==0|| C==0)return 0;vector<int> memo(C+1,-1);//將n*c改為1*c for(int j=0; j<=C; j++)memo[j]=(j>=w[0] ? v[0] : 0);for(int i=1; i<n; i++)for(int j=C; j>=w[i]; j--){//0-i這些物品,背包為j //從n*c的復雜度變成了2*c的復雜度 memo[j]=max(memo[j],v[i]+memo[j-w[i]]);}return memo[C]; } }; int main(){return 0;}
0-1背包問題的變種
1 完全背包問題:每個物品可以無限使用
定容量,每個物品使用功能次數(shù)有最大值,對每個物品可以考慮使用二進制進行表示
2 多重背包問題:每個物品不止一個,有num[i]個
3 多維費用背包問題:要考慮物品的體積和重量兩個維度?
物品間加入更多約束
物品間可以互相排斥;也可以互相依賴
9-7
leetcode 416
//數(shù)組分割成相等的兩部分,其實也是變相的0-1背包問題,容量為sum/2
#include<iostream>
#include<vector>using namespace std;class Solution{
private://memo[i][c]表示是否使用索引[0...i]的這些元素,是否可以完全填充一個容量為c的背包//-1 表示未計算,0表示不可以填充,1表示可以填充 vector<vector<int>> memo; //s使用nums[0...index],是否可以完全填充一個容量為sum的背包 bool tryPartition(const vector<int> &nums, int index, int sum){if(sum==0)return true;if(sum<0 || index<0)return false;if(memo[index][sum] !=-1)return memo[index][sum]==1;memo[index][sum]=(tryPartition(nums,index-1,sum) || tryPartition(nums,index-1,sum-nums[index])) ? 1 : 0 ;return memo[index][sum]==1;//等于1,則為true,反之為false }
public:bool canPartition(vector<int>& nums){int sum =0;for(int i=0;i<nums.size();i++){assert(nums[i]>0);sum+=nums[i];}if(summ%2 !=0)return false;memo=vector<vector<int>>(nums.size(),vector<int>(sum/2)); //表示memo存儲了nums.size()這么多行,每一行是一個向量,并且每一行有sum/2這么多元素 tryPartition(nums,nums.size()-1,sum/2);}
}; int main(){return 0;
}
轉(zhuǎn)換成動態(tài)規(guī)劃問題
//數(shù)組分割成相等的兩部分,其實也是變相的0-1背包問題,容量為sum/2
//轉(zhuǎn)換成動態(tài)規(guī)劃問題
#include<iostream>
#include<vector>
#include<cassert> using namespace std;class Solution{public:bool canPartition(vector<int>& nums){int sum =0;for(int i=0;i<nums.size();i++){assert(nums[i]>0);sum+=nums[i];}if(summ%2 !=0)return false;int n=nums.size();int C=sum/2;vector<boo> memo(C+1,false);for(int i=-; i<=C;i++){memo[i]=(nums[0]==i);for(int i=1; i<n;i++)for(int j=C,j>=nums[i];j--)memo[j]=memo[j] || memo[j-nums[i]];return memo[C];}}
}; int main(){return 0;
}
練習: leetcode 322 硬幣換現(xiàn)
leetcode 377 數(shù)組湊數(shù)
leetcode 474 01串
leetcode 139 字符串連接
leetcode 494 給定整數(shù)sum
9-8 9
leetcode 300最長上升子序列的長度
邊界情況:當兩個元素是否相等的情況,這種情況他是否算在里面
初始化為1
根據(jù)狀態(tài)方程更新各個長度值
最后掃描一組的最大值
//最長上升子序列 //o(n^2)
#include
#include
using namespace std;
class Solution{
public:
int lengthOfLIS(vector& nums){
if(nums.size()==0)return 0;//memo[i] 表示nums[i]為結(jié)尾的最長上升子序列的長度 vector<int> memo(nums.size(), 1);for(int i=1; i<nums.size(),i++)for(int j=0; j<i; j++)if(nums[j]<nums[i])memo[i]=max(memo[i],1+memo[j]);int res=1;for(int i=0; i<nums.size(); i++)res=max(res,memo[i]);return res; } }; int main(){return 0;}
思考
leetcode: 376 升降輪流序列
最長公共子序列
longgest common sequence
圖論中最短路徑
總結(jié)
以上是生活随笔為你收集整理的玩转算法之面试第九章-动态规划的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。