牛客网刷题汇总 递归和动态规划 C++实现

1:斐波那契系列问题的递归和动态规划解决。

1)给定整数N,返回斐波那契数列第N项

//递归实现 ;T(n)=O(2^n)
int f1(int N){
if(N==0)return 0;
if(N==1||N==2)return 1;
return f1(N - 1) + f1(N - 2);
}

//动态规划实现 T(n)=O(n) S(n)=O(n)
int f2(int N){
   int dp[N+1];//静态数组
   dp[0]=0;
   dp[1]=1;
   for(int i=2;i<=N;i++)
       dp[i]=dp[i-1]+dp[i-2];
   return dp[N];
}

//动态规划实现,不额外开辟空间 T(n)=O(n)
int f3(int N){
    if(N<1)return 0;
    if(N==1||N==2)return 1;

    int pre=1,cur=1,res;
    for(int i=3;i<=N;i++){
       res=pre+cur;
       pre=cur;
       cur=res;
    }
    return res;
}

Fibonacci数列 时间复杂度为 O(logN)解法

class Solution {
public:
    //求解矩阵m的p次方
    vector<vector<int>> matrixPower(vector<vector<int>> m, int p) {
        //m是个方阵
        int len = m.size();
        //初始化全为0
        vector<vector<int>> res(len, vector<int>(len, 0));
        for (int i = 0; i < len; i++)
            res[i][i] = 1;//对角阵,相当于整数1
        vector<vector<int>>temp=m;
        for(;p!=0;p>>=1){
            if((p&1)!=0)
                res=mulMatrix(res,temp);
            temp=mulMatrix(temp,temp);//比特串为0则temp翻倍,为1则为奇数串,与res相乘
        }
        return res;
    }

    //计算两矩阵相乘
    vector<vector<int>> mulMatrix(vector<vector<int>> m1, vector<vector<int>> m2) {
        int m = m1.size();
        int p = m2[0].size();
        int n = m1[0].size();
        vector<vector<int>> res(m, vector<int>(p, 0));

        //两矩阵相乘 m*n n*p
        //要注意谁在外层
        for (int j = 0; j < m; j++) {
            for (int k = 0; k < p; k++) {
                for (int i = 0; i < n; i++) {
                    res[j][k] += m1[j][i] * m2[i][k];
                }
            }
        }
        return res;
    }

    //求解斐波那契数列,调用之前的辅助函数
    int Fibonacci(int N){
        if(N<1)return 0;
        if(N==1||N==2)return 1;
      
    //状态转移矩阵为{[1,1],[1,0]}
        vector<vector<int>>base(2, vector<int>(2, 1));
        base[1][1]=0;
        vector<vector<int>>res=matrixPower(base,N-2);
        return res[0][0]+res[1][0];

    }

};

2)台阶问题

递推关系式为S(n)=S(n-1)+S(n-2),与Fibonacci问题求解方法一样

3)估计成熟的牛的数目问题

递推关系为C(n)=C(n-1)+C(n-3),是一个三阶递推序列。利用矩阵求解方法如下。

状态转移方程为{[1,1,0],[0,,0,1],[1,0,0]}

 int cowNum(int N){
        if(N<1)return 0;
        if(N==1||N==2||N==3)return N;
        int a[3][3]={{1,1,0},{0,0,1},{1,0,0}};
        vector<vector<int>>base(3,vector<int>(3,-1));
        for(int i=0;i<3;i++)
            for(int j=0;j<3;j++)
                base[i][j]=a[i][j];//利用二维数组给vector赋初值
        vector<vector<int>>res=matrixPower(base,N-3);
        return 3*res[0][0]+2*res[1][0]+res[2][0];
        
    }

 

2:矩阵的最小路径和

class Solution{
public:
    //经典动态规划问题 T(n)=O(m*n)
    int minPathSum1(vector<vector<int>>m){
      if(m.size()==0||m[0].size()==0)
          return -1;
      int row=m.size();
      int col=m[0].size();
      int dp[row][col];
      dp[0][0]=m[0][0];
      //最左侧一列和最上面一行的最短路径分别为竖直方向和水平方向
      //提前处理
      for(int i=1;i<row;i++)
          dp[i][0]=dp[i-1][0]+m[i][0];
      for(int i=1;i<col;i++)
          dp[0][i]=dp[0][i-1]+m[0][i];

      for(int i=1;i<row;i++){
          for(int j=1;j<col;j++){//每一步最优->全局最优
              dp[i][j]=min(dp[i-1][j],dp[i][j-1])+m[i][j];
          }
      }
      return dp[row-1][col-1];
    }

    //进阶版动态规划求解,S(n)=O(col)
    //利用滚动数组的方法
    int minPathSum2(vector<vector<int>>m){
        if(m.size()==0||m[0].size()==0)
            return -1;
        int row=m.size();
        int col=m[0].size();
        int arr[col];
        arr[0]=m[0][0];

        //先处理第一行
        for(int i=1;i<col;i++)
           arr[i]=arr[i-1]+m[0][i];
        for(int i=1;i<row;i++){
      //处理逐行遍历的首个元素,即更新arr[0]
            arr[0]=arr[0]+m[i][0];
            for(int j=1;j<col;j++){
                arr[j]=min(arr[j-1],arr[j])+m[i][j];
            }
        }
        return arr[col-1];
    }

};

 

3:换钱的最少货币数

class Solution{
public:
    //类似求解矩阵的最小路径和 S(n)=T(n)=O(N*aim)
   int minCoins1(vector<int>arr,int aim){
       if(arr.empty()||aim==0)
           return -1;
       int len=arr.size();
       int max=INT_MAX;
       vector<vector<int>>dp(len,vector<int>(aim+1,max));
       for(int i=0;i<len;i++)
           dp[i][0]=0;
       for(int j=1; j <= aim; j++){
           dp[0][j]=max;//处理第一行,dp[0][j-arr[0]]!=max意思为满减?
           if(j-arr[0]>=0&&dp[0][j-arr[0]]!=max)
               dp[0][j]=dp[0][j-arr[0]]+1;
       }
       int left=0;
       for(int i=1;i<len;i++){
           for(int j=1;j<=aim;j++){
               left=max; //二维数组遍历
               if(j-arr[i]>=0&&dp[i][j-arr[i]]!=max)
                   left=dp[i][j-arr[i]]+1;
               dp[i][j]=min(left,dp[i-1][j]);
           }
       }
    return dp[len-1][aim]!=max?dp[len-1][aim]:-1;
   }

   //利用滚动数组实现优化
   //T(n)=O(n*aim) S(n)=O(aim)
   int minCoins(vector<int>arr,int aim){
        if(arr.empty()||aim==0)
            return -1;
        int len=arr.size();
        int max=INT_MAX;
        int dp[aim+1];
        dp[0]=0;
        for(int j=1;j<=aim;j++){
            dp[j]=max;
            if(j-arr[0]>=0&&dp[j-arr[0]]!=max)
                dp[j]=dp[j-arr[0]]+1;
        }
        
        int left=0;
        for(int i=1;i<len;i++){
            for(int j=1;j<=aim;j++){
               left=max;
               if(j-arr[i]>=0&&dp[j-arr[i]]!=max)
                   left=dp[j-arr[i]]+1;
               dp[j]=min(left,dp[j]);
            }
        }
        return dp[aim]!=max?dp[aim]:-1;

    }
};

 

4:换钱的方法数

class Solution{
public:

    //暴力递归
    int coins1(vector<int>arr,int aim){
        if(aim<1||arr.empty())
            return 0;
        return process1(arr, 0, aim);
    }
    int process1(vector<int>arr, int index, int aim){
        int res=0;
        if(index==arr.size())
            res=(aim==0)?1:0;
        for(int i=0;arr[index]*i<=aim;i++)
            res+= process1(arr, index + 1, aim - arr[index] * i);
        return res;
    }

    //带有记忆化搜索的递归方式
    int coins2(vector<int>arr,int aim){
        if(arr.empty()||aim<0)
            return -1;

        vector<vector<int>>map(arr.size()+1,vector<int>(aim+1,0));
        return process2(arr,0,aim,map);

    }
    int process2(vector<int>arr,int index,int aim,vector<vector<int>>map){
        int res=0;
        if(index==arr.size()){
            res=aim==0?1:0;
        }
        else{
            int mapValue=0;
            for(int i=0;arr[index]*i<=aim;i++){
                mapValue=map[index+1][aim-arr[index]*i];
                if(mapValue!=0){
                    res+=mapValue==-1?0:mapValue;
                } else{
                    res+=process2(arr,index+1,aim-arr[index]*i,map);
                }
            }
        }
        map[index][aim]=res==0?-1:res;
        return res;
    }




};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值