前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >震惊!东某吃葡萄时竟然吃出一道算法题🤔

震惊!东某吃葡萄时竟然吃出一道算法题🤔

作者头像
labuladong
发布2021-09-23 10:52:15
3300
发布2021-09-23 10:52:15
举报
文章被收录于专栏:labuladong的算法专栏

学算法认准labuladong 东哥带你手把手撕力扣😏

今天在牛客网上做了一道叫做「吃葡萄」的题目,非常有意思:

有三种葡萄,每种分别有a, b, c颗,现在有三个人,第一个人只吃第一种和第二种葡萄,第二个人只吃第二种和第三种葡萄,第三个人只吃第一种和第三种葡萄。

现在给你输入a, b, c三个值,请你适当安排,让三个人吃完所有的葡萄,算法返回吃的最多的人最少要吃多少颗葡萄

题目链接:

https://www.nowcoder.com/questionTerminal/14c0359fb77a48319f0122ec175c9ada

牛客网的题目形式和力扣不一样,我去除输入和输出的处理,题目核心就是让你实现这样一个函数:

代码语言:javascript
复制
// 输入为三种葡萄的颗数,可能非常大,所以用 long 型
// 返回吃的最多的人最少要吃多少颗葡萄
long solution(long a, long b, long c);

题目解析

首先来理解一下题目,你怎么做到使得「吃得最多的那个人吃得最少」?

可以这样理解,我们先不管每个人只能吃两种特定葡萄的约束,你怎么让「吃得最多的那个人吃得最少」?

显然,只要平均分就行了,每个人吃(a+b+c)/3颗葡萄。即便不能整除,比如说a+b+c=8,那也要尽可能平均分,就是说一个人吃 2 颗,另两个人吃 3 颗。

综上,「吃得最多的那个人吃得最少」就是让我们尽可能地平均分配,而吃的最多的那个人吃掉的葡萄颗数就是(a+b+c)/3向上取整的结果,也就是(a+b+c+2)/3

PS:向上取整是一个常用的算法技巧。大部分编程语言中,如果你想计算M除以NM / N会向下取整,你想向上取整的话,可以改成(M+(N-1)) / N

好了,刚才在讨论简单情况,现在考虑一下如果加上「每个人只能吃特定两种葡萄」的限制,怎么做?

也就是说,每个人只能吃特定两种葡萄,你也要尽可能给三个人平均分配,这样才能使得吃得最多的那个人吃得最少。

这可复杂了,如果用X, Y, Z表示这三个人,就会发现他们组成一个三角关系:

你让某一个人多吃某一种葡萄,就会产生连带效应,想着就头疼,这咋整?

思路分析

反正万事靠穷举呗,我一开始想了下回溯算法暴力穷举的可能性:

对于每一颗葡萄,可能被谁吃掉?有两种可能呗,那么我写一个回溯算法,把所有可能穷举出来,然后求个最值行不行?

理论上是可行的,但是暴力算法的复杂度一般都是指数级,如果你以葡萄为「主角」进行穷举,看看变量a, b, c都是 long 型的数据,这个复杂度已经让我脊梁沟冒冷汗了。

那么这道题还是得取巧,思路还是要回到如何「尽可能地平均分配」上面,那么事情就变得有意思起来🤣

如果把葡萄的颗数a, b, c作为三条线段,它们的大小作为线段的长度,想一想它们可能组成什么几何图形?我们的目的是否可以转化成「尽可能平分这个几何图形的周长」?

三条线段组成的图形,那不就是三角形嘛?不急,我们小学就学过,三角形是要满足两边之和大于第三边的,假设a < b < c,那么有下面两种情况:

如果a + b > c,那么可以构成一个三角形,只要取每条边的中点,就一定可以把这个三角形的周长平分成三份,且每一份都包含两条边:

也就是说,这种情况下,三个人依然是可以平均分配所有葡萄的,吃的最多的人最少可以吃到的葡萄颗数依然是(a+b+c+2)/3

如果a + b <= c,这三条边就不能组成一个封闭的图形了,那么我们可以将最长边c「折断」,也就是形成一个四边形。

这里面有两种情况:

对于情况一,a + bc的差距还不大的时候,可以看到依然能够让三个人平分这个四边形,那么吃的最多的人最少可以吃到的葡萄颗数依然是(a+b+c+2)/3

随着c的不断增大,就会出现情况二,此时c > 2*(a+b),由于每个人口味的限制,X顶多吃完ab,为了尽可能平分,c边需要被YZ平分,也就是说此时吃的最多的人最少可以吃到的葡萄颗数就是(c+1)/2,即平分c边向上取整。

以上就是全部情况,翻译成代码如下:

代码语言:javascript
复制
long solution(long a, long b, long c) {
    long[] nums = new long[]{a, b, c};
    Arrays.sort(nums);
    long sum = a + b + c;

    // 能够构成三角形,可完全平分
    if (nums[0] + nums[1] > nums[2]) {
        return (sum + 2) / 3;
    }
    // 不能构成三角形,平分最长边的情况
    if (2 * (nums[0] + nums[1]) < nums[2]) {
        return (nums[2] + 1) / 2;
    }
    // 不能构成三角形,但依然可以完全平分的情况
    return (sum + 2) / 3;
}

至此,这道题就被巧妙地解决了,时间复杂度仅需 O(1),关键思路在于如何尽可能平分。

谁又能想到,吃个葡萄得借助几何图形?也许这就算法的魅力吧…

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-08-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 labuladong 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 学算法认准labuladong 东哥带你手把手撕力扣😏
    • 题目解析
      • 思路分析
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档