zoukankan      html  css  js  c++  java
  • codeforces 1251D Salary Changing (二分+贪心)

    (点击此处查看原题)

    题意分析

      一共有s元钱,要用这些钱给n个人发工资,发给每个人的工资si有最少和最多限制 si ∈[li,ri],在发给n个人的总工资小于s的情况下,要求发给n个人中的工资的中位数最大,并求出这个最大的中位数(数据满足:∑li <= s )

    解题思路

    首先注意到发给n个人的工资的中位数mid和n个人所得总工资∑si 具有线性相关性,即 ∑si ∝ mid ,所以我们可以通过二分mid 求出求出满足条件的最优解

    随后我们需要判断当前所得的中位数mid是否满足条件 ,显然,只要给至少n/2 + 1个人发的工资si >= mid ,并且∑si <= s ,则此mid满足条件

    那么为了满足条件,我们会想着让更多的人发放的工资大于等于mid,同时∑si <= s,为此,我们这样设计判断函数check:

    (1)预处理:因为每个人的工资至少有l[i] ,那么我们先为每个人分配最低工资l[i],求得余下总工资 s = s - ∑l[i] 

    (2)预处理:将n个人的工资按最低工资升序排序

    (3)使用的变量:

      cnt:记录发放工资大于等于mid的总人数

      sum:记录剩余的用于发放的钱

    (4)处理方法:对于当前发放工资的中位数mid,从最低工资大的人开始向最低工资小的人枚举

      1、若l[i] >= mid

        显然,给这个人发放l[i]的工资即可,cnt++

      2、若l[i] < mid <= r[i] && sum >= mid - l[i] 

        cnt++,sum -= mid - l[i],表示将这个人的工资设置为mid,同时减去多给这个人的工资mid-l[i]

        此时会有个疑惑,即为什么发给这个人的不是最低工资?这是一种贪心的思想,因为我们将n个人按最低工资升序排序,同时反向枚举n个人,这样保证了先枚举到的人的mid-l[i]更小,也就是说,我们用最少的代价使得更多的人的工资大于等于mid,这样一来,最后只需要判断cnt 是否大于等于 n/2 + 1 即可

    代码区

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<queue>
    #include<string>
    #include<fstream>
    #include<vector>
    #include<stack>
    #include <map>
    #include <iomanip>
    
    #define bug cout << "**********" << endl
    #define show(x, y) cout<<"["<<x<<","<<y<<"] "
    #define LOCAL = 1;
    using namespace std;
    typedef long long ll;
    const ll inf = 1e18 + 7;
    const int Max = 4e5 + 10;
    
    struct Salary
    {
        int l,r;
        bool operator<(const Salary& s) const
        {
            return this->l < s.l;
        }
    }salary[Max];
    
    int n;
    ll s;
    
    bool check(ll mid)
    {
        int cnt = 0;
        ll sum = s;
        for(int i = n ;i >= 1; i --)
        {
            if(salary[i].l >= mid)
            {
                cnt++;
            }
            else if(salary[i].r >= mid && sum >= mid - salary[i].l)
                // 为了让更多的人的工资大于mid,因此从最低工资大的人开始向前枚举,这样造成的额外工资最小,
                // 即总发放的工资最小
            {
                cnt++;
                sum -= mid - salary[i].l;
            }
        }
        return cnt >= n/2 + 1;
    }
    
    int main()
    {
    #ifdef LOCAL
    //    freopen("input.txt", "r", stdin);
    //    freopen("output.txt", "w", stdout);
    #endif
        int t;
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d%lld",&n,&s);
            for(int i = 1;i<= n ;i ++)
            {
                scanf("%d%d",&salary[i].l,&salary[i].r);
                s -= salary[i].l;                    //保证必定取下界
            }
            sort(salary+1,salary+1+n);
            ll l = salary[n/2+1].l,r = inf;
            while(l <= r)
            {
                ll mid = (l + r)>>1;
                if(check(mid))
                    l = mid +1;
                else
                    r = mid -1;
            }
            printf("%lld
    ",r);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    ora04063:view view_test has errors
    toad for oracle中登入界面Connect using框和TNSNames Editor框的显示状态
    格式化时间,转为中文
    一些简单有用的方法合集
    C#加密解密
    计算员工有效工作时间(C#版)
    计算员工有效工作时间(sql版)
    JavaScript 语言基础知识点总结(思维导图)
    正则表达式
    C#中请不要混淆引用类型和ref引用传参
  • 原文地址:https://www.cnblogs.com/winter-bamboo/p/11759199.html
Copyright © 2011-2022 走看看