有一根长度为 n
个单位的木棍,棍上从 0
到 n
标记了若干位置。例如,长度为 6 的棍子可以标记如下:
给你一个整数数组 cuts
,其中 cuts[i]
表示你需要将棍子切开的位置。
你可以按顺序完成切割,也可以根据需要更改切割的顺序。
每次切割的成本都是当前要切割的棍子的长度,切棍子的总成本是历次切割成本的总和。对棍子进行切割将会把一根木棍分成两根较小的木棍(这两根木棍的长度和就是切割前木棍的长度)。请参阅第一个示例以获得更直观的解释。
返回切棍子的 最小总成本 。
解题思路:
①定义一个int型m变量来存储切棍子的次数,并将cuts数组按升序排序 。
int m = cuts.size(); // 要切割的次数
sort(cuts.begin(), cuts.end());
②进行任意一次切割时,待切木棍的左端要么是初始位置0,要么是之前某一次切割的位置;待切木棍的右端要么是末端位置n,要么是之前某一次切割的位置。因此在cuts数组头加上0以及末尾加上n。以便计算成本。
cuts.insert(cuts.begin(), 0); // 在cuts数组头部加0
cuts.push_back(n); // 在cuts数组尾部添加n
// 创建有个(m+2)*(m+2)的二维数组f[i][j],该数组表示切割最小成本
vector<vector<int>> f(m+2, vector<int>(m+2));
③用f[i][j]表示在当前待切割的木棍的左端点为 cuts[i−1],右端点为 cuts[j+1] 时,将木棍全部切开的最小总成本。
④为了得到最小总成本,定义一个int型k表示枚举第一刀的位置。如果第一刀的位置为 cuts[k],其中 k∈[i,j],那么我们会将待切割的木棍分成两部分,左侧部分的木棍为 cuts[i−1..k],对应的可以继续切割的位置为 cuts[i..k−1];右侧部分的木棍为 cuts[k..j+1],对应的可以继续切割的位置为cuts[k+1..j]。
// 从待切木棍最后一个点开始
for (int i = m; i >= 1; i--) {
for (int j = i; j <= m; j++) {
// 若i==j,则表示只要切一个点,成本只有待切木棍的长度;若不是,则先置f[i][j]为无穷大
f[i][j] = (i == j ? 0 : INT_MAX);
// 分析在待切木棍上的各个待切点,取成本最小
for (int k = i; k <= j; k++) {
f[i][j] = min(f[i][j], f[i][k-1] + f[k+1][j]);
}
f[i][j] += cuts[j+1] - cuts[i-1];
}
}
⑤每次切的成本都是cuts[j+1]−cuts[i−1]。
f[i][j] += cuts[j+1] - cuts[i-1];
⑥最后返回从f[1][m]。
return f[1][m];