剑指 offer 第 24 天

第 24 天

数学(中等)

剑指 Offer 14- I. 剪绳子

给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m-1] 。请问 k[0]*k[1]*...*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。

示例 1:

输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1

示例 2:

输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36

提示:

  • 2 <= n <= 58

题解思路:动态规划、贪婪算法

动态规划:不断计算切一刀后乘积的变化

class Solution {
    public int cuttingRope(int n) {
        // 记录长度为 i 的绳子乘积最大值
        int[] dp = new int[n + 1];
        dp[2] = 1;
        // 从绳子长度为 3 开始计算,因为长度为 2 时为1
        for (int i = 3; i <= n; i ++) {
            // 计算减长度为 j 的一刀后是否继续减,如果剪完 j 后继续剪,乘积为 dp[i-j]*j,如果不减,则乘积为 (i-j)*j
            for (int j = 2; j < i; j ++) {
                dp[i] = Math.max(dp[i], Math.max(dp[i-j]*j, (i-j)*j));
            }
        } 
        return dp[n];
    }
}

复杂度:时间 O(n*n) 空间 O(n)

贪婪算法:获得尽可能多的 3,可以用求导证明

class Solution {
    public int cuttingRope(int n) {
        if(n < 4){
            return n - 1;
        }
        int res = 1;
        while(n > 4){
            res *= 3;
            n -= 3;
        }
        return res * n;
    }
}

复杂度:时间 O(n) 空间 O(1)

剑指 Offer 57 - II. 和为s的连续正数序列

输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。

序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。

示例 1:

输入:target = 9
输出:[[2,3,4],[4,5]]

示例 2:

输入:target = 15
输出:[[1,2,3,4,5],[4,5,6],[7,8]]

限制:

  • 1 <= target <= 10^5

题解思路:滑动窗口

滑动窗口:双指针构成滑动窗口,搜索所有可以的集合

class Solution {
    public int[][] findContinuousSequence(int target) {
        int i = 1, j = 2;
        int sum = 3;
        List<int[]> res = new ArrayList<>();
        while(i < j) {
            if(sum > target) {
                sum -= i;
                i ++;
            }
            else if (sum < target){
                j ++;
                sum += j;
            }
            else if (sum == target) {
                int[] ans = new int[j - i + 1];
                for(int k = i; k <= j; k++) {
                    ans[k - i] = k;
                }
                res.add(ans);
                sum -= i;
                i++;
            }
        }
        return res.toArray(new int[0][]);
    }
}

复杂度:时间 O(n) 空间 O(n)

剑指 Offer 62. 圆圈中最后剩下的数字

0,1,···,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字。

例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。

示例 1:

输入: n = 5, m = 3
输出: 3

示例 2:

输入: n = 10, m = 17
输出: 2

限制:

  • 1 <= n <= 10^5
  • 1 <= m <= 10^6

题解思路:模拟、动态规划、递归

模拟:循环队列模拟

class Solution {
    public int lastRemaining(int n, int m) {
        ArrayList<Integer> list = new ArrayList<>(n);
        for (int i = 0; i < n; i++) {
            list.add(i);
        }
        int idx = 0;
        while (n > 1) {
            idx = (idx + m - 1) % n;
            list.remove(idx);
            n--;
        }
        return list.get(0);
    }
}

复杂度:时间 O(n) 空间 O(n)

动态规划:

class Solution {
    public int lastRemaining(int n, int m) {
        int f = 0;
        for (int i = 2; i != n + 1; ++i) {
            f = (m + f) % i;
        }
        return f;
    }
}

复杂度:时间 O(n) 空间 O(1)

递归:

class Solution {
    public int lastRemaining(int n, int m) {
        return f(n, m);
    }

    public int f(int n, int m) {
        if (n == 1) {
            return 0;
        }
        int x = f(n - 1, m);
        return (m + x) % n;
    }
}

复杂度:时间 O(n) 空间 O(n)

posted @ 2021-09-28 20:15  起床睡觉  阅读(18)  评论(0编辑  收藏  举报