1 问题
给你一根长度为 n 的绳子,请把绳子剪成整数长的 m 段( m 、 n 都是整数, n > 1 并且 m > 1 , m <= n ),每段绳子的长度记为 k[1],…,k[m] 。请问 k[1]k[2]…*k[m] 可能的最大乘积是多少?例如,当绳子的长度是 8 时,我们把它剪成长度分别为 2、3、3 的三段,此时得到的最大乘积是 18 。
由于答案过大,请对 998244353 取模。
数据范围:
2≤n≤10^14
进阶:空间复杂度 O(1) , 时间复杂度 O(logn)
示例1
输入:4
返回值:4
说明:拆分成 2 个长度为 2 的绳子,2 * 2 = 4
示例2
输入:5
返回值:6
说明:剪成一个长度为 2 的绳子和一个长度为 3 的绳子,答案为2*3=6
示例3
输入:874520
返回值:908070737
2 答案
这题直接不会
动态规划,不对,超出内存限制
class Solution:
def cutRope(self , number: int) -> int:
if number <= 3:
return number - 1
dp = [0] * (number + 1)
dp[1], dp[2], dp[3], dp[4] = 1, 2, 3, 4
for i in range(5, number+1):
for j in range(1, i):
dp[i] = max(dp[i], dp[i - j] * dp[j]) % 998244353
return dp[-1]
贪心,不对,超出内存限制
class Solution:
def cutRope(self , number: int) -> int:
if number <= 3:
return number - 1
res = 1
while number > 4:
res *= 3
res %= 998244353
number -= 3
res = res*number
return res % 998244353
https://blog.csdn.net/CSDNLHCC/article/details/134083170?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522170100442516800213049168%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=170100442516800213049168&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2blogfirst_rank_ecpm_v1~rank_v31_ecpm-1-134083170-null-null.nonecase&utm_term=%20%E5%89%AA%E7%BB%B3%E5%AD%90&spm=1018.2226.3001.4450
官方解,快速幂+快速乘法
主要考察快速幂+快速乘法
根据均值不等式,有: n 1 + n 2 + … + n m m ≥ n 1 n 2 … n m m \frac{n_1+n_2+\ldots+n_m}{m} \geq \sqrt[m]{n_1 n_2 \ldots n_m} mn1+n2+…+nm≥mn1n2…nm ,等号当且仅当 n 1 = n 2 = n 3 = … n m n_1=n_2=n_3=\ldots n_m n1=n2=n3=…nm 时成立,因为加法部分和是固定的绳子总长度,因此要使乘积最大,应该以相等的长度等分成多段。
如果将绳子按照每段长度为x等分成m段,则n=mx,乘积为 x m x^m xm ,因为有 x m = x n x = ( x 1 x ) n x^m=x^{\frac{n}{x}}=\left(x^{\frac{1}{x}}\right)^n xm=xxn=(xx1)n,因此当 x 1 x x^{\frac{1}{x}} xx1 取最大值时取最大值。
令 y = x 1 x y=x^{\frac{1}{x}} y=xx1,即求这个函数的极值即可直到绳子等分成长度为多少可以使乘积最大。根据取对数、求导、求极值等一系列数学操作,得驻点为 x 0 = e x_0=e x0=e,即极大值需要将绳子分成每段e,但是绳子长度只能是整数,靠近e的只有2 和3,二者分别代入公式,发现当x=3时,乘积达到最大。
因此后续,使用贪心思想,不断将绳子分成每段长度为3即可,不足3的可以考虑,如果最后剩余的是2,直接乘上,如果最后剩余的是1,则取出一个3组成4分成长度为2的两段,因为 2 ∗ 2 > 1 ∗ 3 2 * 2>1 * 3 2∗2>1∗3
step 1:将问题分成三种情况,使用快速幂和快速乘法直接计算幂。
step 2:n整除3的时候,即可以全部完成分成长度为3的小段,一共n/3段,计算
3
n
/
3
3^{n / 3}
3n/3 即可。
step 3:n除3余1的时候,需要拿出一个3个1组合称,一共n/3−1段长度为3的,2段长度为2的,计算
2
∗
2
∗
3
n
/
3
−
1
2 * 2 * 3^{n / 3-1}
2∗2∗3n/3−1即可;
step 4:n除3余2的时候,直接将剩下长度为2的段乘在之前的乘积上,计算
2
∗
3
n
/
3
−
1
2 * 3^{n / 3-1}
2∗3n/3−1即可。
计算幂为了缩短时间,采用快速幂加快速乘法优化:
快速幂:如果我们要计算 5 10 5^{10} 510 ,常规的算法是5∗5=25,然后再25∗5=125,如此往下,一共是9次运算,即n−1次。但是我们可以考虑这样:5∗5=25(二次)、25∗25=625(四次)、625∗625=…(八次),这是一个二分的思维,运算次数缩减到了 n次,
快速乘法:直接计算会
x
a
x^{a}
xa超出long的表示范围,因此我们可以考虑用加法来代替乘法,并在这其中取模。就比如
a
∗
b
=
a
∗
(
b
1
+
b
2
+
b
3
+
.
.
.
)
a * b=a *(b_1+b_2+b_3+...)
a∗b=a∗(b1+b2+b3+...),其中
b
i
b_i
bi是数字b的二进制各位,假设a=5,b=110101,我们有
a∗b=a∗(100000∗1+10000∗1+1000∗0+100∗1+10∗0+1∗1)=(a∗100000)∗1+(a∗10000)∗1+(a∗1000)∗0+(a∗100)∗1+(a∗10)∗0+(a∗1)∗1
class Solution:
def __init__(self):
self.mod = 998244353
def fast(self, x: int, y: int) -> int:
res = 0
x %= self.mod
y %= self.mod
while y:
if y & 1:
res += x
if res >= self.mod:
res -= self.mod
y = y >> 1
x = x << 1
if x >= self.mod:
x -= self.mod
return res
def Pow(self, x: int, y: int) -> int:
res = 1
while y:
if y & 1:
res = self.fast(res, x)
x = self.fast(x, x)
y = y >> 1
return res
def cutRope(self , number: int) -> int:
if number <= 3:
return number -1
if number % 3 == 0:
return self.Pow(3, number // 3)
elif number % 3 == 1:
return self.fast(self.Pow(3, number // 3 - 1), 4)
else:
return self.fast(self.Pow(3, number // 3), 2)
https://www.nowcoder.com/share/jump/9318638301701008192892