算法---动态规划

一:记忆化搜索

动态规划---设计感和艺术感较强,可解决问题广泛,应用灵活的算法

先看一下: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);
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值