有这样一类问题,给你一个长度为n的绳子,要求你可以剪切任意次数,分为任意段,使得这些子段长度的乘积最大。我们把这类问题暂时先称为剪绳子,这种问题的解法也很简单,通过数学证明可以得出,我们优先剪长度为3的子段,假设剪了x个3后,剩余的长度小于3时,有以下三种情况:
情况一:剩余长度为0,则此时最优,答案为3的x次方。
情况二:剩余长度为1,此时我们应该少剪一个3,即剪切x-1个3,剩余长度为4,此时结果最优,答案为3的x-1次方乘以4。
情况三:剩余长度为2,此时也是最优,答案为3的x次方乘以2。
因此很容易写出代码,这个问题就可以提交了,代码如下
#include <iostream>
using namespace std;
long long M = 1000000007;
int main() {
long long n;
cin >> n;
// x表示的是n分解出来的3的个数
long long x = n / 3;
int y = n % 3; // y表示的是分为x个3后的余数
/*
* 如果y=0,则说明刚好分为x个3
* 如果y=1,则需要调整一下,拿出一个3,和1加起来是4,这样分为x-1个3和1个4是最优解
* 如果y=2,则x个3和1个2刚好是最优解
* */
if (y == 1) {
y = 4;
x--; // 拿出之前分解的一个3,和剩余的一个1组合在一起,这样结果最优
}
long long ans = y;
if (ans == 0) ans = 1; // 这里防止y=0的情况,如果y为0,则需要将ans置1,因为后面都是乘法运算
for (int i = 0; i < x; i++) {
ans *= 3; // 暴力解决
if (ans >= M) ans %= M;
}
return 0;
}
但是此时又有一个问题,如果给的n特别大,比如,n<=10^18,那么如果用暴力的循环x次求解,则一定会超时,所以此时需要借助快速幂思想来求解。
思考一下,现在想计算a^b,如何求解,可以第一时间想到库函数,比如c++中,可以通过pow(a, b)计算,但是b特别大,比如b=10^17,此时pow函数肯定会越界,一般对于这种大数量级的幂运算,很容易越界,所以题目会要求你对某个数取余,最常见的是对M=10^9+7取余。所以需要在运算过程中动态对M取余,快速幂的思想是,每次将底数a更新为a*a,指数b减半,过程如下:
第一步:
第二步:我们需要将看成一个整体,把看成一个整体,继续运用第一步的思想可以得到:
依照上面的步骤,指数每次折半,底数每次变为平方,时间复杂度为logn。
上面有个问题,如果指数为奇数,怎么办?在c++中,比如指数b=7,那么7/2=3,会丢掉一个底数,所以分情况,如果指数为奇数,那么,如果为偶数,则
代码如下:
#include <iostream>
using namespace std;
long long M = 1000000007;
int main() {
long long n;
cin >> n;
// x表示的是n分解出来的3的个数
long long x = n / 3;
int y = n % 3; // y表示的是分为x个3后的余数
/*
* 如果y=0,则说明刚好分为x个3
* 如果y=1,则需要调整一下,拿出一个3,和1加起来是4,这样分为x-1个3和1个4是最优解
* 如果y=2,则x个3和1个2刚好是最优解
* */
if (y == 1) {
y = 4;
x--; // 拿出之前分解的一个3,和剩余的一个1组合在一起,这样结果最优
}
long long ans = y;
if (ans == 0) ans = 1; // 这里防止y=0的情况,如果y为0,则需要将ans置1,因为后面都是乘法运算
// 通过快速幂解决超时问题
long long tmp = 3; // tmp为上面说的底数,每次循环都会更新为自己的平方,因此tmp的值变化依次为:3,9,27,81,....
// 在整个过程中,用到快速幂解决超市
// tmp为底,底会指数增大,x为幂,会指数减少
while(x > 0) {
// 对于奇数次幂,如果要多于的底数先与结果ans做运算
// 注意一个点:对于任何指数b>=2的数字,每次除以2,最终b一定会等于1,那么此时,指数为奇数,会进入if分支,将底数乘给最终结果ans;
if(x % 2 == 1) {
ans *= tmp;
ans %= M;
}
x /= 2;
tmp *= tmp;
tmp %= M;
}
cout << ans;
return 0;
}
我们拿一个case走一下代码,看看中间过程值分别是多少,比如3^7,此时tmp=3,x=7;
进入代码我们看每次循环的各个变量的值
循环前:x=7,ans=1,tmp=3
第1次循环后:x=3,ans=ans*tmp=3=,tmp=tmp*tmp=9=第2次循环后:x=1,ans=ans*tmp=27=,tmp=tmp*tmp=81=
第3次循环后:x=0,ans=ans*tmp=2187=,tmp=tmp*tmp=6561=
循环结束,输出ans=2187
有个题目正好用来练手:OnlineJudge