首页 > 试题广场 >

牛妹的招聘

[编程题]牛妹的招聘
  • 热度指数:217 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
一年一度的校招来了,同学们都有一个聪明值Si(-1000<=Si<=1000)和勤奋值Fi(-1000<=Fi<=1000).
牛妹会选择一些同学作为同事,希望选入所有人的Si+Fi的总和最大,并且选中的所个人聪明值与勤奋值的和不能是负数
给出每个同学分别的聪明值和勤奋值,请帮助牛妹求出最大的总和。
示例1

输入

5,[-5,8,6,2,8],[7,-6,-3,1,-5]

输出

8

说明

选择第1,3,4位同学,聪明值的总和是-5+6+2=3,勤奋值的总和是7-3+1=5,最终输出8。
注意,,虽然1,2,3,4同学的聪明值和勤奋值总和是10,但是勤奋值的总和是负数,这种情况不可以。

备注:
人数小于100
输入第一个参数:人数
输入第二个参数:每个同学的聪明值
输入第三个参数:每个同学的勤奋值

感觉他这例题给的就不对:
选1、3、4、5四个同学最大是11且没有负数
发表于 2021-09-04 18:28:18 回复(2)
    朴素做法是普通dp。dp[i][j]代表第0-i人的总S值为j时F的最大值,然后返回max{dp[n-1][j]+j}即可。但是这个朴素做法有个问题,就是有大量的无效S值,因为所有Si的组合的总和是离散的,有很多S值根本不能可能取到,所以这个做法实际上效率比较低。顺着这个思路,既然无效值很多,那我不妨计算一下所有有效的(S,F)组合,那么就得到了下一个做法。
    设初始时的(S,F)的组合为{(0,0)},往后一个人一个人的加,更新组合。比如当前组合是{(s0, f0),(s1,f1)....},然后遇到了(s,f)这个人,那么之前的组合更新为{(s0, f0),(s1,f1)....}U{(s0+s, f0+f),(s1+s,f1+f)....},然后再纳入下一个人重复上述过程。最后统计max(S+F)即可。但是这个状态数是2的次幂级别的,毫无疑问会爆空间,需要进一步的优化筛去一些不需要的(s,f)组合。(如果之前做过单调栈的题,到这里思路就很清晰了)
    更进一步分析组合的更新过程,可以发现一些有趣的性质。当遍历到第k人时,如果(si,fi)和(sj,fj)满足,si<sj&&fi<fj,那么(si,fi)这个组合就没必要保存了。因为假设之后的某种人员选择(k以后的人怎么选)能使得(si,fi)这个组合演化成最大和,那么用(sj,fj)直接替换(si,fi)一定能演化成更大的和,矛盾,所以可以直接不要这个(si,fi)。这里涉及到单调性的性质,因此单调栈的使用就很显然了。按s排个序,然后对f用单调栈筛选即可。(上面是我自己做这个题的时候的思考过程,刚开始写了个朴素dp,感觉复杂度肯定达不到要求。然后很快意识到了这个单调性质就是不知道代码怎么写,纠结了一会,才想好怎么组织代码。。。还是太菜了)
class Solution {
public:
    /**
     * 
     * @param number int整型 参加校招的同学人数
     * @param si int整型一维数组 这个同学的聪明值
     * @param siLen int si数组长度
     * @param fi int整型一维数组 这个同学的勤奋值
     * @param fiLen int fi数组长度
     * @return int整型
     */
    int smartSum(int number, int* si, int siLen, int* fi, int fiLen) {
        // write code here
        vector<pair<int,int>> a, b;
        a.push_back({0, 0});
        for(int i = 0; i < number; ++i){
            int sz = a.size();
            for(int j = 0; j < sz; ++j) a.push_back({a[j].first+si[i], a[j].second+fi[i]});
            sort(a.begin(), a.end());
            for(int j = 0; j < a.size(); ++j){
                while(!b.empty() && b.back().second <= a[j].second) b.pop_back();
                b.push_back(a[j]);
            }
            swap(a, b);
            b.clear();
        }
        
        int ans = 0;
        for(int i = 0; i < a.size(); ++i)
            if(a[i].first >= 0 && a[i].second >= 0)
                ans = max(ans, a[i].first + a[i].second);
        return ans;
    }
};


编辑于 2020-03-13 21:33:56 回复(0)

问题信息

难度:
2条回答 963浏览

热门推荐

通过挑战的用户

查看代码