一:记忆化搜索
动态规划---设计感和艺术感较强,可解决问题广泛,应用灵活的算法
先看一下:Fibonacci数列的执行效率
#include<iostream>
#include<ctime>
using namespace std;
//Fibonacci数列----F(0)=0,F(1)=1,F(n)=F(n-1)+F(n-2)
int col=0;
int fib(int n){
col++;
if(n==0) return 0;
if(n==1) return 1;
return fib(n-1)+fib(n-2);
}
int main(){
/*测试程序性能*/
int n=42;
time_t startTime=clock();
int res=fib(n);//执行
time_t endTime=clock();
cout<<"fib("<<n<<")="<<res<<endl;
cout<<"time:"<<double(endTime-startTime)/CLOCKS_PER_SEC<<"s"<<endl;
cout<<"运行递归函数fib():"<<col<<"次数"<<endl;
return 0;
}
是一个很不高效过程---因为这课递归树很长,而且重复计算特别多。
解决方法---引入向量数组,记忆所有执行结果
#include<iostream> #include<ctime> #include<vector> using namespace std; //Fibonacci数列----F(0)=0,F(1)=1,F(n)=F(n-1)+F(n-2) vector<int> memo; int fib(int n){ if(n==0) return 0; if(n==1) return 1; if(memo[n]==-1) memo[n]=fib(n-1)+fib(n-2); return memo[n]; } int main(){ /*测试程序性能*/ int n=10; memo=vector<int>(n+1,-1);//初始化memo time_t startTime=clock(); int res=fib(n);//执行 time_t endTime=clock(); cout<<"fib("<<n<<")="<<res<<endl; cout<<"time:"<<double(endTime-startTime)/CLOCKS_PER_SEC<<"s"<<endl; return 0; }
这叫---记忆化搜索,存储每一次结果
自下而上的解决问题:----先解决小数据量,在得出大数据量
int fib(int n){
vector<int> memo=vector<int>(n+1,-1);//初始化memo
memo[0]=0;
memo[1]=1;
for(int i=2;i<=n;i++)
memo[i]=memo[i-1]+memo[i-2];
return memo[n];
}
二:动态规划
将原问题拆分成若干子问题,同时存储子问题结果保证子问题只求解一次,最后求出原问题 |
三:爬楼梯
比如:三层楼梯---【1,1,1】,【1,2】,【2,1】
记忆化搜索
#include<iostream> #include<ctime> #include<vector> using namespace std; class Solution { private: vector<int> memo; int get_way(int n){ if(n==0||n==1) return 1; if(memo[n]==-1) memo[n]=get_way(n-1)+get_way(n-2); return memo[n]; } public: int climbStairs(int n) { memo=vector<int>(n+1,-1); return get_way(n); } }; int main(){ cout<<Solution().climbStairs(4); return 0; }
动态规划
#include<iostream> #include<ctime> #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]; } }; int main(){ cout<<Solution().climbStairs(4); return 0; }
四:三角形最小路径和
这里注意一个细节:不是每次选最小的,而是当前元素想走只有左右两种选择。
所以上一次的结果对下一次影响
不妨把这些数按照下标的位置当做索引:7为(0,0),1为(2,1)
首先(0,0)的7可以走(1,0)的3和(1,1)的8,所以路径和【10,18】
然后:(1,0)的3可以走(2,0)的8和(2,1)的1
注意:(2,1)的1既可以被(1,0)的3走到,又可以被(1,1)的8走到,所以取最小
总之:路径第3行为【18,11,18】,最后选11
#include<iostream> #include<vector> #include<algorithm> using namespace std; class Solution { public: int minimumTotal(vector<vector<int>>& triangle) { int m=triangle.size(); //定义路径和数组path,每一行记录当前走到的最短路径 vector<vector<int>> path(m,vector<int>(m));//path数组为m行m列 //初始状态 path[0][0]=triangle[0][0]; //记下来由上一行更新这一行的结果 //注意特点:当前行最左一定是上一行最左到达的,当前行最右一定是上一行最右到达的 //除左右两端,中间的数可有两种,需要计算取最小 for(int i=1;i<m;i++)//从第二行开始,第一行是初始状态 for(int j=0;j<=i;j++)//遍历该行 { if(j==0)//最左端 path[i][j]=path[i-1][j]+triangle[i][j]; else if(j==i)//最右端 path[i][j]=path[i-1][j-1]+triangle[i][j]; else//取最小 path[i][j]=min(path[i-1][j-1],path[i-1][j])+triangle[i][j]; } //取path的最后一行的最小值 return *min_element(path[m-1].begin(),path[m-1].end());} }; int main(){ vector<vector<int>> triangle={ {2}, {1,3}, {5,4,7}, {9,2,6,1} }; cout<<Solution().minimumTotal(triangle); return 0; }
当然因为j要么最左,要么最右,可以减少判断
for(int i=1;i<m;i++)//从第二行开始,第一行是初始状态 { path[i][0]=path[i-1][0]+triangle[i][0]; for(int j=1;j<i;j++)//遍历该行 { path[i][j]=min(path[i-1][j-1],path[i-1][j])+triangle[i][j]; } path[i][i]=path[i-1][i-1]+triangle[i][i]; }
法二---递归
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
class Solution {
private:
vector<vector<int>> triangle;
int pathLeastSum(int i,int j) {
if(i==triangle.size()) return 0;//递归结束
return //当前结点可以向左走,也可以向右走
triangle[i][j]+min(pathLeastSum(i+1,j),pathLeastSum(i+1,j+1));
}
public:
int minimumTotal(vector<vector<int>>& triangle) {
this->triangle=triangle;
return pathLeastSum(0,0);//从(0,0)开始出发
}
};
int main(){
vector<vector<int>> triangle={
{1},
{3,1},
{-1,-2,-3},
{9,7,6,8}
};
cout<<Solution().minimumTotal(triangle);
return 0;
}
发现思考发现是自顶向上,也就是说从当前元素出发,思考下一次的递归结果,然后利用递归结束条件。缺点是效率满
记忆化搜索
class Solution { private: vector<vector<int>> memo; int colMinPath(vector<vector<int>>& triangle,int i,int j){ if(i==triangle.size()) return 0; if(memo[i][j]){ return memo[i][j]; } return memo[i][j]=triangle[i][j]+ min(colMinPath(triangle,i+1,j),colMinPath(triangle,i+1,j+1)); } public: int minimumTotal(vector<vector<int>>& triangle) { memo=vector<vector<int>>(triangle.size(),vector<int>(triangle.size())); return colMinPath(triangle,0,0); } };
class Solution { public: int dfs(vector<vector<int>>& triangle, int i, int j, vector<unordered_map<int, int>>& memo) { if (i == triangle.size()) return 0; if (memo[i].count(j)) return memo[i][j]; return memo[i][j] = min(dfs(triangle, i + 1, j, memo), dfs(triangle, i + 1, j + 1, memo)) + triangle[i][j]; } int minimumTotal(vector<vector<int>>& triangle) { vector<unordered_map<int, int>> memo(triangle.size()); return dfs(triangle, 0, 0, memo); } };