整数问题(求和 拆分 替换等) leetcode问题

1. 829. 连续整数求和

给定一个正整数 N,试求有多少组连续正整数满足所有数字之和为 N?

例 1:

输入: 5
输出: 2
解释: 5 = 5 = 2 + 3,共有两组连续整数([5],[2,3])求和后为 5。

示例 2:

输入: 9
输出: 3
解释: 9 = 9 = 4 + 5 = 2 + 3 + 4

示例 3:

输入: 15
输出: 4
解释: 15 = 15 = 8 + 7 = 4 + 5 + 6 = 1 + 2 + 3 + 4 + 5

解法1: 暴力 超时 😣

class Solution:
    def consecutiveNumbersSum(self, N: int) -> int:
        count = 1  # 等于1: 最后一个N的情况;
        end = (N+1)//2
        for i in range(1, end):  # 这种方法可行,但超时, 怎么优化
            sum_add = 0
            test = []
            for j in range(i, N+1):
                sum_add = sum_add + j
                if sum_add == N:
                    count += 1
                    break
                elif sum_add > N:
                    break
        return count

解法2:   变成了数学题求解, 难受😭

假设连续q到p相加等于N, 则满足:

N=\sum_{i=1}^pi - \sum_{i=1}^qi=\frac{(1+p)*p}{2} -\frac{(1+q)*q}{2}  

推出: 

2N=p+p^2-q-q^2=p-q+(p-q)*(p+q)=(p-q)*(p+q+1)

a=p-qb=p+q+1, 则a,b为2N的因子(因子:a*b=2N),则有:

a+b=2p+1

a-b=-(2q+1)

2p+1为奇数,可以a+b为奇数,则a,b必须一个为奇数一个为偶数,则转变为 求a,b是2N 的因子,而且是一奇一偶数【写代码的点】;

其中a*b=2N, a小,可以推出a\leq \sqrt{2N}, 若a> \sqrt{2N},则a*b > 2N;

若要求出p,q则公式为: p = \frac{a+b-1}{2}q=\frac{b-a-1}{2}

class Solution:
    def consecutiveNumbersSum(self, N: int) -> int:
        # a,b必是一奇一偶
        import math
        a = 1
        res = 0
        end = int(math.sqrt(2*N))
        while a <= end:
            if 2*N % a == 0: # i是2N的因子
                b = 2*N // a
                if a%2 + b%2 == 1: # a,b是一奇一偶数
                    # q = (a+b-1)//2
                    # p = (b-a+1) // 2
                    # print(p, q)  # 找到的 连续数组开始 结尾的数
                    res += 1
            a = a + 1
        return res 

参考:此处

解法3:  大神思路;

class Solution:
    def consecutiveNumbersSum(self, N: int) -> int:
        # 解题思路:
        # 当1个数时, 必然有一个N
        # 当2个数时,x,x+1相加等于N, 则(N-1)//2 ==0,则必存在解;
        # 当3个数时,x,x+1,x+2相加等于N,则(N-2-1)//3==0,则必存在解;
        # 当4个数时,x,x+1,x+2,x+3相加等于N, 则(N-3-2-1)==0,必存在解;
        i = 1 
        res = 0
        while N > 0:  # 判断条件设置的 极为精妙;
            if N % i == 0:
                res += 1
            N = N - i  #
            i = i + 1
        return res

2. 整数分解

给定一个正整数n,一个正整数k,一个可行的数字组合是一组正整数,它们的和为n,且组合中数字个数小于等于k,组合中允许数字重复出现 输出:所有可能的组合数(两个组合中出现的数字相同,顺序不同计为同一个); 此题和连续整数求和相比是 去掉了 连续 这个条件;

解法1: ??

3.343. 整数拆分

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

示例 1:

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

示例 2:

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

说明: 你可以假设 不小于 2 且不大于 58。

解法1:  动态规划dp; 动态规划推导公式:dp[i]=max(dp[i], dp[j]*dp[i-j])

if n <= 3:  # 
    return 1*(n-1)
dp = [0]*(n+1)  # dp[i]表示i拆分成至少两个整数的和,这些整数的乘积最大值
dp[2:n+1] = list(range(2, n+1))  # 初始化为 原始值;
# 递归公式i拆分成j和i-j, dp[i]=dp[j]*dp[i-j]
for i in range(4, n + 1):
    for j in range(i):  # 求dp[i]遍历完i之前所有值
        dp[i] = max(dp[i], dp[j] * dp[i - j])  # 动态规划 总有这个条件;
return dp[n]

解法2:  动态规划dp; 动态规划还是不好理解, 需要对题目进行记忆,

对于的正整数 n,当n\geq 2时,可以拆分成至少两个正整数的和。令 k 是拆分出的第一个正整数,则剩下的部分是 n-kn-k 可以不继续拆分,或者继续拆分成至少两个正整数的和。

创建数组dp,其中 dp[i] 表示将正整数i拆分成至少两个正整数的和之后,这些正整数的最大乘积。特别地,0不是正整数,1是最小的正整数,0 和 1 都不能拆分,因此dp[0]=dp[1]=0

class Solution:
    def integerBreak(self, n: int) -> int:
        # 1. 动态规划方法一
        dp = [0]*(n+1)   # 0,1不能拆分,dp[0]=0, dp[1]=0
        for i in range(2, n+1):
            for j in range(i):   # 求出dp[i] # 最大值; 
                dp[i] = max(dp[i], j * (i-j), j*dp[i-j])   
        return dp[n]

4. 397. 整数替换

给定一个正整数 n ,你可以做如下操作:

如果 n 是偶数,则用 n / 2替换 n 。
如果 n 是奇数,则可以用 n + 1或n - 1替换 n 。
n 变为 1 所需的最小替换次数是多少?

示例 1:

输入:n = 8
输出:3
解释:8 -> 4 -> 2 -> 1

示例 2:

输入:n = 7
输出:4
解释:7 -> 8 -> 4 -> 2 -> 1
或 7 -> 6 -> 3 -> 2 -> 1

示例 3:

输入:n = 4
输出:2

提示:

  • 1 <= n <= 231 - 1

解法1: 好不容尝试dp, 超内存,超时间, n太大,不该使用dp;

class Solution:
    def integerReplacement(self, n: int) -> int:
        # n非常大,存在超时的问题
        if n == 1:
            return 0
        if n == 2:
            return 1
        dp = [0]*(n+1) # dp[1]=0, dp[2]=1
        dp[1] = 0
        dp[2] = 1
        for i in range(3, n+1):
            if i %2 == 0:
                dp[i] = dp[i//2] + 1
            else:
                dp[i] = min(dp[(i+1)//2]+1, dp[(i-1)//2]+1) + 1
        return dp[n]

解法2:  递归

class Solution:
    def integerReplacement(self, n: int) -> int:
        # 纯递归; 看到题 应该超那个思路想(1.dp, 2.递归,3.贪心)还是不会; 被当时最熟悉的思路(之前做过的题影响)
        if n <= 1:
            return 0
        if n <= 2:
            return 1
        if n % 2 == 0: # 偶数
            return self.integerReplacement(n//2) + 1 
        else:
            return min(self.integerReplacement(n+1), self.integerReplacement(n-1)) + 1

5. 273. 整数转换英文表示

 

6. 39. 组合总和

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

说明:

所有数字(包括 target)都是正整数。
解集不能包含重复的组合。 
示例 1:

输入:candidates = [2,3,6,7], target = 7,
所求解集为:
[
  [7],
  [2,2,3]
]

示例 2:

输入:candidates = [2,3,5], target = 8,
所求解集为:
[
  [2,2,2,2],
  [2,3,3],
  [3,5]
]

解法1:  递归 +回溯+ 去重复

class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        # 递归
        result = []
        def digui(target, target_list):
            for item in candidates:
                if target - item == 0:  # 可以相等记录下来
                    tem = target_list+[item]
                    if sorted(tem) not in result:  #  去重复 
                        result.append(sorted(tem))
                elif target - item > 0:
                    digui(target-item, target_list+[item]) # 传参时, 不要加进去
                else:
                    pass
        digui(target, [])
        # temp = []
        # for item in result:  # 去重复的问题  用sorted
        #     if sorted(item) not in temp:  
        #         temp.append(sorted(item))
        return result

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值