【LeetCode】整数拆分

267 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第13天,点击查看活动详情

整数拆分

原题:leetcode-cn.com/problems/in…

描述

给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。返回你可以获得的最大乘积。

难度

中等

示例

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

思路

对于一个正整数 n,当正整数 n >= 2 时,至少可以拆分成两个正整数的和。设 i 是拆分出来的第一个正整数,n - i 可以拆分也可以不拆分,每个正整数拆分后对应的整数的最大乘积取决于比它小的正整数拆分后对应的最大乘积,所以可以采用动态规划求解。

创建数组 dp,dp[i] 表示正整数 i 拆分成至少两个正整数之后,这些整数乘积最大,0 和 1 不能拆分,所以 dp[0] = dp[1] = 0。当 i >= 2 时,第一个拆分出来的正整数为 j,剩下的正整数 i - j,就有两种情况:

  • 当 i - j 不能继续拆分时,乘积为 j * (i - j)
  • 当 i - j 可以继续拆分时,乘积为 j * dp[i - j]

此时 dp[i] = max{j * (i - j), j * dp[i - j]},因每次 j 是从 1 到 i - 1中取值,所以每次迭代得到的值也要加入判断,可以得到状态转移方程:

dp[i] = max{dp[i], max{j * (i - j), j * dp[i - j]}}

最后 dp[n] 就是拆分成至少两个正整数后,这些整数的最大乘积。

代码

Rust

pub struct Solution {}
​
impl Solution {
    pub fn integer_break(n: i32) -> i32 {
        let mut dp = vec![0; n as usize + 1];
        for i in 2..=n as usize {
            for j in 1..i {
                // 求拆分成 j 和 i - j 或者 j 和 i - j (能继续拆分)
                // 和上一次拆分 dp[i] (也就是拆分成 1, j - 1 ) 中的最大值
                dp[i] = dp[i].max((j as i32 * (i - j) as i32).max(j as i32 * dp[i - j]));
            }
        }
        dp[n as usize]
    }
}
#[test]
fn test_integer_break() {
    let n = 2;
    println!("{}", n);
​
    let max = Solution::integer_break(n);
    println!("{}", max);
​
    let n = 10;
    println!("{}", n);
​
    let max = Solution::integer_break(n);
    println!("{}", max);
}

运行结果:

2
1
10
36

Go

func integerBreak(n int) int {
    dp := make([]int, n + 1)
    for i := 2; i <= n; i++ {
        for j := 1; j < i; j++ {
            // 求拆分成 j 和 i - j 或者 j 和 i - j (能继续拆分)
            // 和上一次拆分 dp[i] (也就是拆分成 1, j - 1 ) 中的最大值
            dp[i] = max(dp[i], max(j * (i - j), j * dp[i - j]))
        }
    }
    return dp[n]
}
​
func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}
func TestIntegerBreak(t *testing.T) {
    n := 2
    t.Log(n)
​
    max := integerBreak(n)
    t.Log(max)
​
    n = 10
    t.Log(n)
​
    max = integerBreak(n)
    t.Log(max)
}

运行结果:

2
1
10
36

Java

public class Main {
    public static int integerBreak(int n) {
        int[] dp = new int[n + 1];
        for (int i = 2; i <= n; i++) {
            for (int j = 1; j < i; j++) {
                // 求拆分成 j 和 i - j 或者 j 和 i - j (能继续拆分)
                // 和上一次拆分 dp[i] (也就是拆分成 1, j - 1 ) 中的最大值
                dp[i] = Math.max(dp[i], Math.max(j * (i - j), j * dp[i - j]));
            }
        }
        return dp[n];
    }
​
    public static void main(String[] args) {
        int n = 2;
        System.out.println(n);
​
        int max = integerBreak(n);
        System.out.println(max);
​
        n = 10;
        System.out.println(n);
​
        max = integerBreak(n);
        System.out.println(max);
    }
}

运行结果:

2
1
10
36