题目陈述
大意:给定两种若干数量的蛋糕和一些盘子,问所有的分法中,蛋糕数量最少的盘子中分到最多的蛋糕数量是多少。所有分法应该满足:同一个盘子不装有两种蛋糕、每个盘子都有蛋糕。
算法一:朴素做法
算法思路
- 为了不多次重复冗余的文字,接下来我们约定,将蛋糕数量最少的盘子中分到最多的蛋糕数量称为 a n s ans ans。
- 我们先这样分析,如果有 x x x个盘子,只有一种蛋糕,数量为 a a a,那么所有分法的 a n s ans ans就是 a / x a/x a/x
- 为什么?此处我们感性理解一下,取 a n s ans ans的时候,一定是所有盘子的最大值和最小值的差值是最小的,如果不是最小的,将最大值的几个蛋糕,移动到最小值上面,我们又得到了更大的 a n s ans ans,与我们当初的定义相互矛盾。
- 我们从 1 − x 1-x 1−x依次一个位置一个位置放蛋糕,循环的放置,直至放完位置,这样放置出来的必然是最优情况,此时最大值和最小值的差值至多为 1 1 1,将最大值上面的一个移动到最小值,则完成了互换,即此时最大值和最小值的差值稳定,即无法减小,即最大值和最小值的差值是最小的
- 这种情况,即我们计算有 x x x个盘子,只有一种蛋糕,数量为 a a a,的 a n s ans ans,按照上面的分法,只需要 x / n x/n x/n即可计算出来 a n s ans ans
- 那么两种蛋糕如何处理?同理,枚举第一种蛋糕用的盘子 i i i,剩下的 n − i n-i n−i种盘子给第二种蛋糕,二者各自的 a n s ans ans再取一个最小值,即可得到该分法的 a n s ans ans
- i i i线性遍历 [ 1 , n − 1 ] [1,n-1] [1,n−1]即可获取所有方案
- 此处可能有同学想问,是否存在只用一种蛋糕就得到了所有分法的 a n s ans ans?因为题目数据保证了 2 ≤ n ≤ a + b 2\leq n \leq a+b 2≤n≤a+b,故不存在只有一种蛋糕分法而得到所有分法的 a n s ans ans的情况。
代码实现
class Solution
{
public:
int splitCake(int n, int a, int b)
{
int aAns, bAns, ans = -1;
//第一种单独的ans,第二种单独的ans,总的ans
for (int i = 1; i < n; i ++)
{
aAns = a / i; //求解第一种的ans
bAns = b / (n - i); //求解总的ans
if(aAns > 0 && bAns > 0) //两种单独的方法都合法
ans = max(ans, min(aAns, bAns)); //求解总得ans
}
return ans;
}
};
复杂度分析
- 时间复杂度,一个for循环从 1 − n 1-n 1−n,复杂度为 O ( n ) O(n) O(n)
- 空间复杂度,定义了变量 a A n s , b A n s , a n s aAns,bAns,ans aAns,bAns,ans,复杂度为 O ( 1 ) O(1) O(1)
算法二:二分法
-
我们设一种分法中第一种、第二种蛋糕分别占用了 a N u m , b N u m aNum,bNum aNum,bNum个盘子,每个盘子内的蛋糕数量分别为 a i ( 1 ≤ i ≤ a N u m ) , b i ( 1 ≤ i ≤ b N u m ) a_i(1\leq i \leq aNum),b_i(1 \leq i \leq bNum) ai(1≤i≤aNum),bi(1≤i≤bNum)
-
则有 ∑ a i = a , ∑ b i = b \sum a_i = a,\sum b_i=b ∑ai=a,∑bi=b
-
且 a i ≥ a m i n a_i\geq a_{min} ai≥amin,所以 a i a m i n ≥ 1 \cfrac{a_i}{a_{min}}\geq 1 aminai≥1则有, a 1 a m i n + a 2 a m i n + . . . + a a N u m a m i n ≥ a a m i n ≥ a N u m \cfrac{a_1}{a_{min}}+\cfrac{a_2}{a_{min}}+...+\cfrac{a_{aNum}}{a_{min}}\geq \cfrac{a}{a_{min}}\geq aNum amina1+amina2+...+aminaaNum≥amina≥aNum
-
故有 a a m i n ≥ a N u m \cfrac{a}{a_{min}}\geq aNum amina≥aNum
-
故同理有 b b m i n ≥ b N u m \cfrac{b}{b_{min}}\geq bNum bminb≥bNum
-
显然最后的 a n s ans ans会取二者中更小的一个,即 a n s = m i n ( a m i n , b m i n ) ans=min(a_{min},b_{min}) ans=min(amin,bmin)
-
若方案合法则有 a a m i n + b b m i n ≥ a N u m + b N u m = n \cfrac{a}{a_{min}}+ \cfrac{b}{b_{min}}\geq aNum +bNum=n amina+bminb≥aNum+bNum=n
-
即 a a m i n + b b m i n ≥ n \cfrac{a}{a_{min}}+ \cfrac{b}{b_{min}}\geq n amina+bminb≥n
-
易得 a m i n ( a m i n , b m i n ) + b m i n ( a m i n , b m i n ) ≥ n \cfrac{a}{min(a_{min},b_{min})}+ \cfrac{b}{min(a_{min},b_{min})}\geq n min(amin,bmin)a+min(amin,bmin)b≥n
-
即 a a n s + b a n s ≥ n \cfrac{a}{ans}+ \cfrac{b}{ans}\geq n ansa+ansb≥n
-
假设此处的 a n s ans ans我们用 i i i来枚举,我们可以知道对于答案有这样一个性质, 1 ≤ i ≤ a n s 1\leq i\leq ans 1≤i≤ans都是合法方案, a n s < i ≤ n ans<i\leq n ans<i≤n都不合法,如图
-
对于前者(其实题目没有说一定需要放完所有蛋糕),我们将最小值的位置移动一块到最大值的位置即可构造更小的情况
-
对于后者,反证法,如果 a n s < i ≤ n ans<i\leq n ans<i≤n合法,则与已有条件矛盾,故得证。
-
即区间具有二段性,可以使用二分法。
-
此时我们对 a i + b i ≥ n \cfrac{a}{i}+ \cfrac{b}{i}\geq n ia+ib≥n进行二分答案即可
代码实现
class Solution
{
public:
int splitCake(int n, int a, int b)
{
int l = 1, r = min(a, b), mid; //左边界,右边界,中点
当a<b,a中无法拿出b个蛋糕来,反之亦然,故右边界需要取小的那个
while (l < r) //l == r的时候,代表已经找出答案
{
mid = (l + r + 1) / 2; //向上取整,避免死循环
if (check(n, a, b, mid)) //该分法合法,说明可以取更大的值
l = mid; //左边界缩小
else //该分法不合法,说明只能取最小的值
r = mid - 1; //右边界缩小
}
return l; //返回答案
}
bool check(int &n, int &a, int &b, int x) //二分条件
{
return a / x + b / x >= n; //两种蛋糕每盘都分最小的,若合法总和应该不小于n
}
};
复杂度分析
- 时间复杂度,为二分答案的复杂度,最多二分 log m i n ( a , b ) − 1 \log min(a,b) - 1 logmin(a,b)−1次,复杂度为 O ( log m i n ( a , b ) ) O(\log min(a,b)) O(logmin(a,b))
- 空间复杂度,定义了 l , r , m i d l,r,mid l,r,mid,为 O ( 1 ) O(1) O(1)