秋招准备100天---01

日期:2019.5.17 星期五 多云
今天只看了一些算法题,C++还有深度学习都没有看,该打。。。。没完成任务

斐波那契数列题目及扩展

对于一些问题类似于斐波那契数列的,可以写成

f(n) = f(n-1) + f(n-2)

之类的递推式,是可以有三种解题思路的。

  • 最简单的就是直接套公式,时间复杂度都是O(2^N)
  • 第二种就是循环遍历,(比如f(1) = 1,f(2) = 1,那么令a = 1,b = 1,tmp = a + b; 从第3个数开始向后计算,f(3) = a + b,然后a = b,b = f(3);f(4) = a + b;一直到f(n);这种方法的时间复杂度是O(N)
  • 最绝妙的是时间复杂度为O(logN),看到了logN,那么一定有关二进制,二分法之类的。这里就要用到矩阵快速幂。
问题1

求一个数的n次幂
如果是循环n次,那么时间复杂度太高,这时候就要用到快速幂算法。n看成一个二进制数,那么位数是1的位,进行相乘。比如说n = 5,那么二进制数是101,只需要进行1*m(20) * 0*m(21) * 1*m(22).只需要进行3次运算,不需要进行5次运算。

int kuaisumi(int m,int n) {
    long long res = 1;
    while (n) {
        if (n & 1) res *= m;
        m *= m;
        n = n >> 1;
    }
    return res;
}

问题2


斐波那契数列

f(1) = 1,f(2) = 1,f(n) = f(n-1) + f(n-2) (n >= 3)

方法1:递归,时间复杂度O(2^N)

int fib1(int n) {
    if (n == 1 || n == 2)
        return 1;
    return fib1(n-1) + fib1(n-2);
}

方法2:方法1太多重复的计算,所以改进一下,时间复杂度为O(N)

int fib2(int n) {
    if (n == 1 || n == 2)
        return 1;
    int a = 1,b = 1,res;
    for (int i = 3; i <= n; i++) {
        res = a + b;
        a = b;
        b = res;
    }
    return res;
}

方法3:快速矩阵幂。时间复杂度是O(logN),看到log那么就要用到二分法或者二进制相关算法。斐波那契数列是一个二维递推数列。

在这里插入图片描述

//矩阵乘法
vector<vector<int>> matrixMul(vector<vector<int>>& m1, vector<vector<int>>& m2) {
    int n1 = m1.size(),n2 = m1[0].size(),n3 = m2.size();
    vector<vector<int>> res(n1,vector<int>(n3));
    for (int i = 0; i < n1; i++) {
        for (int j = 0; j < n3; j++) {
            for (int k = 0; k < n2; k++) {
                res[i][j] += m1[i][k] * m2[k][j];
            }
        }
    }
    return res;
}
//快速矩阵幂
vector<vector<int>> matrixPower(vector<vector<int>>& m,int n) {
    vector<vector<int>> res(m.size(),vector<int>(m.size()));
    for (int i = 0; i < m.size(); i++) res[i][i] = 1;
    while (n) {
        if (n & 1) res = matrixMul(res,m);
        m = matrixMul(m,m);
        n = n >> 1;
    }
    return res;
}

int fib3(int n) {
    if (n == 1 || n == 2)
        return 1;
    vector<vector<int>> m = {{1,1},{1,0}};
    vector<vector<int>> tmp = matrixPower(m,n-2);
    return tmp[0][0] + tmp[1][0];
}

问题2

青蛙跳台阶,每次只能跳一阶或者两阶,当台阶数为n的时候,有多少种方式?

f(n) = f(n-1) + f(n-2),f(1) = 1,f(2) = 2;

这道题和斐波那契数列一样。

方法1:时间复杂度O(2^N)

int tj1(int n) {
    if (n == 1 || n == 2)
        return n;
    return tj1(n-1) + tj1(n-2);
}

方法2:时间复杂度O(N)

int tj2(int n) {
    if (n == 1 || n == 2)
        return n;
    int a = 1,b = 2,res;
    for (int i = 3; i <= n; i++) {
        res = a + b;
        a = b;
        b = res;
    }
    return res;
}

方法3:时间复杂度O(log N)

int tj3(int n) {
    if (n == 1 || n == 2)
        return n;
    vector<vector<int>> m = {{1,1},{1,0}};
    vector<vector<int>> tmp = matrixPower(m,n-2);
    return 2 * tmp[0][0] + tmp[1][0];
}
问题3

成熟母牛每年都可以生一只小牛,小牛3年后才可以成为成熟母牛。农场里第一年有一只成熟母牛,计算出n年后农场有多少牛?

这里很明显是有一个递推公式,分析一下,第n年母牛数量来自于n-1年的数量和新出生的数量(n-3年)之和。也就是

f(n) = f(n-1) + f(n-3) f(1)=1,f(2)=2,f(3)=3;

方法1:时间复杂度O(2^N)

int fib1(int n) {
    if (n == 1 || n == 2 || n == 3)
        return n;
    return fib1(n-1) + fib1(n-3);
}

方法2:时间复杂度O(N)

int fib2(int n) {
    if (n == 1 || n == 2 || n == 3)
        return n;
    int a = 1,b = 2,c = 3,res;
    for (int i = 4; i <= n; i++) {
        res = a + c;
        a = b;
        b = c;
        c = res;
    }
    return res;
}

方法3:时间复杂度O(logN),最后递推式在矩阵中怎么计算需要注意,很容易错。

int fib3(int n) {
    if (n == 1 || n == 2 || n == 3)
        return n;
    vector<vector<int>> m = {{1,1,0},{0,0,1},{1,0,0}};
    vector<vector<int>> tmp = matrixPower(m,n-3);
    return 3 * tmp[0][0] + 2 * tmp[1][0] + tmp[2][0]; // 注意这里,怎么得到的这个公式,很容易出错,为什么要2 * tmp[1][0]。
}

矩阵的最小路径和

一个矩阵从左上角开始,只能向右和向下前进,问题到达右下角路径上和最小的那条路径和。


方法1:用动态规划,dp[i][j]表示位置(i,j)最短路径和。那么就会有转移方程如下,需要新建一个M*N的数组,时间复杂度为O(M*N),空间复杂度也是O(M * N)。

dp[i][j] = min(dp[i-1][j],dp[i][j-1]) + nums[i][j] 
dp[0][j] += dp[0][j-1];
dp[i][0] += dp[i-1][0]

方法2: 不需要新建数组,在原数组上进行修改。这时候空间复杂度就为O(1),时间复杂度为O(M*N)


int minDis(vector<vector<int>>& nums) {
    int m = nums.size(),n = nums[0].size();
    for (int i = 1; i < m; i++) nums[i][0] = nums[i-1][0] + nums[i][0];
    for (int j = 1; j < n; j++) nums[0][j] = nums[0][j-1] + nums[0][j];
    for (int i = 1; i < m; i++) {
        for (int j = 1; j < n; j++) {
            nums[i][j] = min(nums[i-1][j],nums[i][j-1]) + nums[i][j];
        }
    }
    return nums[m-1][n-1];
}

方法3: 还是动态规划,但是可以把空间复杂度变为O(min(M,N)).

int minDis2(vector<vector<int>>& nums) {
    int m = nums.size(),n = nums[0].size();
    int more = max(m,n),less = min(m,n);
    vector<int> dp(less,0);
    dp[0] = nums[0][0];
    bool flag = (less == n);
    for (int i = 1; i < less;i++) dp[i] = (flag ? nums[0][i] : nums[i][0]) + dp[i-1];
    for(int i = 1; i < more; i++) {
        dp[0] += flag ? nums[i][0] : nums[0][i];
        for (int j = 1; j < less; j++) {
            dp[j] = min(dp[j-1],dp[j]) + (flag ? nums[i][j] : nums[j][i]);
        }
    }
    return dp[less-1];
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值