问题描述
Give you a rope of length n, please cut the rope into m segments (m and n are integers, n>1 and m>1). The length of each segment is denoted by k[0], k[1], ..., k[m]. What is the maximum possible product of k[0]*k[1]*...*k[m]? And analyze the time complexity of your solution.
翻译
给定一条长度为n的绳子,请将其切割成m段(m和n是整数,n>1且m>1)。每段的长度由k[0],k[1],...,k[m]表示。求k[0]*k[1]*...*k[m]的最大可能乘积,并分析你的解决方案的时间复杂度。
思路
绳子分为两半,前面一半为0~i(正在遍历的部分),后面一般为i~n。每次仅需要对正在遍历的部分进行处理即可,处理方式如下:
0~i的部分可以划分为任意的 (0~j,j+1~i)两端长度,其中j的范围从1~i变化(相当于遍历0~i)
因此需要两层循环,第一层i遍历绳子的长度,第二层j遍历前半段绳子的长度。
对于前半段绳子每次遍历有以下几种情况:
1.dp[i] :表示本次不对绳子进行操作,(说明之前所划分的乘积最大)
2.j*dp[i-j] :表示本次将前半段绳子分成 j、i-j两部分(其中dp[i-j]表示其值来源于之前的划分,说明绳子不止划分成两段)
3. j*(i-j) :将绳子只划分为两段,即j和i-j (特殊情况?)
每次对以上三种情况取最大值 即可得到乘积最大
代码
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
const int N = 1e6 + 10;
int dp[N];
int cut(int n)
{
//绳子长度2~n
for (int i = 2; i <= n; i++) {
for (int j = 1; j <=i; j++)//绳子切割的长度j从 1~i-1
{
//每次遍历都会把当前dp[i]最大值赋给dp[i],dp[i]表示局部乘积最大,如果下一个j所对应局部最大值较大就更新
//然后 j++
//j*(i-j)表示不剪绳子,此时j表示剩下的一部分
//j*dp[i-j]表示剪j这么长,对接下来i-j段继续求解最优解
//三种情况求最大值
/*
1.dp[i]
2.只剪一次 j然后与(i-j)相乘
3.剪完之后i-j还要继续剪,j*dp[i-j] j乘以之前的最优解
*/
dp[i] = max(dp[i], max(j * dp[i - j], j * (i - j)));
}
}
return dp[n];
}
int main()
{
int n;
cin >> n;
dp[1] = 1;
dp[2] = 1;
int res = cut(n);
cout << res << endl;
return 0;
}
测试案例
8
运行结果
2*3*3
18
时间复杂度分析
- 外层循环迭代了
n-2
次(从3到n),内层循环的时间复杂度为 O(i),其中 i 从 1 到 n,因此内层循环的总时间复杂度为 O(n^2)。
速记
三种状态:dp[i]、j*dp[i-j]、j*(i-j)特殊情况
两层循环: i : 2->n
j:1->i