1. 程式人生 > 其它 >劍指 offer 第 24 天

劍指 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)