在你的面前从左到右摆放着n根长短不一的木棍,你每次可以折断一根木棍,并将折断后得到的两根木棍一左一右放在原来的位置(即若原木棍有左邻居,则两根新木棍必须放在左邻居的右边,若原木棍有右邻居,新木棍必须放在右邻居的左边,所有木棍保持左右排列)。折断后的两根木棍的长度必须为整数,且它们之和等于折断前的木棍长度。你希望最终从左到右的木棍长度单调不减,那么你需要折断多少次呢?
输入描述:
第一行是一个数n,表示开始时有多少根木棍(1<=n<=3000)第二行有n个数,从第1个到第n个分别表示从左到右的木棍长度。对任意木棍的长度l,有1<=l<=3000。
输出描述
输出一行,一个数,你最少所需的折断木棍的次数x
示例1
输入:
5
3 5 13 9 12
输出
1
说明
你可以将长度为13的木棍折成长度分别为5和8的两根木棍,最终得到的排列是3 5 5 8 9 12
思路
从后往前遍历,使用单调递减栈,数组中元素从后往前呈递减关系,如果碰到nums[i]>nums[i+1],则将nums[i]折断成若干小于等于栈顶的小木棒,并让他们尽量保持平均大小,将略小的入栈。原理如下:题目中要求的是最小的折断次数,从而要折断的木棍数越少越好。从而对于nums[i]>nums[i+1],一定要对nums[i]做对应处理,且不能对数组整体产生太大影响(如果nums[i]折的太小,会使Nums[i]的前面发生nums[k]>nums[k+1]),因此,思路就是将nums[i]折断后的长度尽量靠近nums[i+1]且保持平均,从而使其对数组整体造成的影响最小
图解
C++单调栈代码
int breakNum(vector<int>& nums)
{
int ans = 0;
for (int i = nums.size() - 2; i >= 0; i--)
{
if (nums[i + 1] >= nums[i]) continue;
int t = (nums[i] - 1) / nums[i + 1];
ans += t;
nums[i] /= (t + 1);
}
return ans;
}
作者:Ikaruga
链接:https://leetcode-cn.com/circle/discuss/eXOcnD/view/SecVmv/
代码如下(python)
nums = [3,5,13,9,12]
res = 0
for i in range(len(nums)-1):
temp = len(nums)-2-i
if nums[temp]<nums[temp+1]:continue
else:
t = nums[temp]//nums[temp+1]
yushu = nums[temp]%nums[temp]+1
res+=t
if yushu==0:nums[temp]=nums[temp]/t
else:nums[temp]=nums[temp]/(t+1)
print(res)