题解 | #吃汉堡#

吃汉堡

http://www.nowcoder.com/practice/09a480cee1364e4180fa304d2b9c78c7

题意整理:

题目给出两个数组a和b。对于一个i,a[i]a[i]a[i]表示第i天的鸡肉汉堡数量,b[i]b[i]b[i]表示第i天的牛肉汉堡数量。要求计算出在每天吃的汉堡总数不同的情况下,且吃到尽量多的汉堡(两种汉堡之和)的情况下尽量少吃牛肉汉堡,在以上前提下最少需要吃的牛肉汉堡数量。

抽象的说,就是求出满足(优先级从上到下)

a[i]+b[i]a[j]+b[j],ija[i]+b[i] \neq a[j]+b[j] ,i\neq ja[i]+b[i]=a[j]+b[j],i=j

i=0n1(a[i]+b[i])max \sum_{i=0}^{n-1} {(a[i]+b[i])} →maxi=0n1(a[i]+b[i])max

i=0n1b[i]min \sum_{i=0}^{n-1} {b[i]} →mini=0n1b[i]min

时的i=0n1b[i] \sum_{i=0}^{n-1} {b[i]}i=0n1b[i]

方法一:排序+优先队列

核心思想:

题目要求在满足每天吃的汉堡数不同的情况下,满足两个极限条件(最多及最少)时的情况下所吃的牛肉汉堡,一般这种极限问题,可以考虑采用贪心的思想.

首先可以将所有天数的两种汉堡数构成的二元组加入优先队列进行排序,排序依据是:首先按汉堡总数排序,之后按鸡肉汉堡数排序.排序完成后,我们贪心地依次取出队列首部元素(既总数最多或在存在多个总数最多的情况下鸡肉汉堡最多的),然后判断与上次吃的总汉堡数的关系,如果小于上次吃的则直接吃掉全部并记录,如果比上次吃的多或相等则需要逐渐抛弃一些汉堡直到比上次吃的少,抛弃汉堡的顺序为先抛弃牛肉汉堡,牛肉汉堡数为0后开始抛弃鸡肉汉堡,下面简单证明这种策略不会导致错误答案.

1).当此次的总汉堡数少于上次的总汉堡数,显然全部吃完是最优的

2).当此次的总汉堡数等于上次的总汉堡数,此时不能够吃这么多否则会畅神冲突,这时可能有两种情况,第一则是减少上一轮吃的总汉堡数,第二种则是减少此次吃的总汉堡数,但由于总汉堡数相同情况下是按照鸡肉汉堡数排序,所以减少此次吃的总汉堡数不会差于减少上次吃的总汉堡数,而可能优于减少上次吃的总汉堡数(因为减少此次吃的总汉堡数可以更多的抛弃牛肉汉堡)

3).此次的总汉堡数多于上次吃的总汉堡数.由于存在不能够吃一样的汉堡数,所以这种情况是存在的.这次采取的策略是不断减少此次的汉堡数直到比上一次少,而不是直接吃下所有汉堡,因为如果直接吃下汉堡必然造成冲突,否则导出矛盾. 设当前汉堡数为m,上一次吃的汉堡数为p,m>pm>pm>p,此时如果能够吃比p更多的汉堡数q,则说明之前未出现过吃q个汉堡的情况,但由于取出汉堡是按总汉堡数排序,总汉堡数相同按鸡肉堡数排序,所以在取出当前元素的上一个元素同样能够取得q个汉堡而不引起冲突,这与之前描述的策略产生冲突,不可能存在,所以只能够取得比上一次更少的汉堡总数.

alt

核心代码:

class Solution {
public:
    struct cmp {
        bool operator()(vector<int>& r1, vector<int>& r2) {
            //自定义排序
            return r1[0] + r1[1] == r2[0] + r2[1] ? r1[0] < r2[0] : r1[0] + r1[1] < r2[0] + r2[1];
        }
    };
    
    long long EatingHamburger(int n, vector<int>& a, vector<int>& b) {
        priority_queue<vector<int>, vector<vector<int>>, cmp> q;
        for(int i = 0; i < n; ++i) {
            q.push({a[i], b[i]});//加入优先队列
        }
        long long ans = 0;
        int p = INT_MAX;
        while(!q.empty()) {
            //不断取出汉堡吃掉
            vector<int> it = q.top();
            q.pop();
            if(it[0] + it[1] < p) {
                //比上一次吃的少,全部吃完即可
                ans += it[1];
                p = it[0] + it[1];
            } else if(it[0] + it[1] == p) {
                //此时需要少吃一个,尽量少吃鸡肉堡
                ans += it[1] == 0 ? 0 : it[1] - 1;
                --p;
            } else {
                //此时既it[0] + it[1] > p,需要至多吃p-1个并少吃鸡肉堡
                ans += it[0] < (p - 1) ? p - 1 - it[0] : 0;
                --p;
            }
            if(p == 0) break;//不可能继续吃
        }
        return ans;
    }
};

复杂度分析:

时间复杂度:O(nlogn)O(nlogn)O(nlogn)。每个元素被放入和取出最多一次,而优先队列原理为堆,其排序消耗为O(nlogn)O(nlogn)O(nlogn)

空间复杂度:O(n)O(n)O(n),既为优先队列的开销

方法二:排序+数组

核心思想:

思路与方法一相同,不过可以通过使用临时数组直接排序而不是借助优先队列,其他部分相同.

核心代码:

class Solution {
public:
    long long EatingHamburger(int n, vector<int>& a, vector<int>& b) {
        vector<vector<int>> res(n, vector<int>(2));
        for(int i = 0; i < n; ++i) {
            //构造数组
            res[i][0] = a[i];
            res[i][1] = b[i];
        }
        //按汉堡总数排序,总数相同按鸡肉堡数量排序
        sort(res.begin(), res.end(), [&](vector<int>& r1, vector<int>& r2) {
            return r1[0] + r1[1] == r2[0] + r2[1] ? r1[0] > r2[0] : r1[0] + r1[1] > r2[0] + r2[1];
        });
        int p = INT_MAX;//记录上一次吃的汉堡总数
        long long ans = 0;
        for(vector<int>& it : res) {
            if(it[0] + it[1] < p) {
                //比上一次吃的少,全部吃完即可
                ans += it[1];
                p = it[0] + it[1];
            } else if(it[0] + it[1] == p) {
                //此时需要少吃一个,尽量少吃鸡肉堡
                ans += it[1] == 0 ? 0 : it[1] - 1;
                --p;
            } else {
                //此时既it[0] + it[1] > p,需要至多吃p-1个并少吃鸡肉堡
                ans += it[0] < (p - 1) ? p - 1 - it[0] : 0;
                --p;
            }
            if(p == 0) break;//不可能继续吃
        }
        return ans;
    }
};

复杂度分析:

时间复杂度:O(nlogn)O(nlogn)O(nlogn)。既为sort排序的开销

空间复杂度:O(n)O(n)O(n),既临时构造的辅助数组的大小

全部评论

相关推荐

避坑恶心到我了大家好,今天我想跟大家聊聊我在成都千子成智能科技有限公司(以下简称千子成)的求职经历,希望能给大家一些参考。千子成的母公司是“同创主悦”,主要经营各种产品,比如菜刀、POS机、电话卡等等。听起来是不是有点像地推销售公司?没错,就是那种类型的公司。我当时刚毕业,急需一份临时工作,所以在BOSS上看到了千子成的招聘信息。他们承诺无责底薪5000元,还包住宿,这吸引了我。面试的时候,HR也说了同样的话,感觉挺靠谱的。于是,我满怀期待地等待结果。结果出来后,我通过了面试,第二天就收到了试岗通知。试岗的内容就是地推销售,公司划定一个区域,然后你就得见人就问,问店铺、问路人,一直问到他们有意向为止。如果他们有兴趣,你就得摇同事帮忙推动,促进成交。说说一天的工作安排吧。工作时间是从早上8:30到晚上18:30。早上7点有人叫你起床,收拾后去公司,然后唱歌跳舞(销售公司都这样),7:55早课(类似宣誓),8:05同事间联系销售话术,8:15分享销售技巧,8:30经理训话。9:20左右从公司下市场,公交、地铁、自行车自费。到了市场大概10点左右,开始地推工作。中午吃饭时间大约是12:00,公司附近的路边盖饭面馆店自费AA,吃饭时间大约40分钟左右。吃完饭后继续地推工作,没有所谓的固定中午午休时间。下午6点下班后返回公司,不能直接下班,需要与同事交流话术,经理讲话洗脑。正常情况下9点下班。整个上班的一天中,早上到公司就是站着的,到晚上下班前都是站着。每天步数2万步以上。公司员工没有自己的工位,百来号人挤在一个20平方米的空间里听经理洗脑。白天就在市场上奔波,公司的投入成本几乎只有租金和工资,没有中央空调。早上2小时,晚上加班2小时,纯蒸桑拿。没有任何福利,节假日也没有3倍工资之类的。偶尔会有冲的酸梅汤和西瓜什么的。公司的晋升路径也很有意思:新人—组长—领队—主管—副经理—经理。要求是业绩和团队人数,类似传销模式,把人留下来。新人不能加微信、不能吐槽公司、不能有负面情绪、不能谈恋爱、不能说累。在公司没有任何坐的地方,不能依墙而坐。早上吃早饭在公司外面的安全通道,未到上班时间还会让你吃快些不能磨蹭。总之就是想榨干你。复试的时候,带你的师傅会给你营造一个钱多事少离家近的工作氛围,吹嘘工资有多高、还能吹自己毕业于好大学。然后让你早点来公司、无偿加班、抓住你可能不会走的心思进一步压榨你。总之,大家在找工作的时候一定要擦亮眼睛,避免踩坑!———来自网友
qq乃乃好喝到咩噗茶:不要做没有专业门槛的工作
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务