首页 > 试题广场 >

发工资

[编程题]发工资
  • 热度指数:1215 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
小度新聘请了一名员工牛牛, 每个月小度需要给牛牛至少发放m元工资(给牛牛发放的工资可以等于m元或者大于m元, 不能低于m)。
小度有一些钞票资金, 一共有n种不同的面额, 对于面额为x_i的钞票, 小度有y_i张, 并且每一个钞票面额都能整除所有比它大的面额, 并且每一张钞票不能找零。
小度想知道这部分资金最多能牛牛发放多少个月的工资?

输入描述:
包括n+1行,第一行包括两个正整数
接下来的n行, 每行两个正整数, 即面额和该面额所拥有的钞票数量。


输出描述:
一个整数,表示最多能支付多少个月工资。
示例1

输入

3 51
100 1
50 4
1 2

输出

4

说明

注意钱不能找零,所以:
100能支付一个月工资
50+1,50+1能支付两个月工资
50+50能支付一个月工资
即最多能支付四个月的工资。
参考评论区写的,根据自己的理解改动了一点
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;


class Money {
    int value;
    int nums;

    public Money(int value, int nums) {
        this.value = value;
        this.nums = nums;
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        List<Money> list = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            int a = sc.nextInt();
            int b = sc.nextInt();
            list.add(new Money(a, b));
        }
        list.sort((a, b) -> b.value - a.value);
        int ans = 0;
        //先把大的直接发出去
        for (Money money : list) {
            if (money.value >= m) {
                ans += money.nums;
                money.nums = 0;
            } else break;
        }

        //然后开始车轮战,每一趟while发出一个月工资
        while (true) {
            int needToPay = m;
            //先从大面额开始涮一轮,在不超m的情况下,尽可能地拿大面额
            for (Money money : list) {
                if (money.nums == 0) continue;
                int needNum = needToPay / money.value;
                needNum = Math.min(needNum, money.nums);//最多取完,不能超了。
                money.nums -= needNum;
                needToPay -= needNum * money.value;
            }
            //刚好凑成了就结束这一趟
            if (needToPay <= 0) {
                ans++;
                continue;
            }
            //再从小面额开始补(此时所有面额都已大于needToPay)
            for (int i = list.size() - 1; i >= 0; i--) {
                Money money = list.get(i);
                if (money.nums == 0) continue;
                money.nums--;
                needToPay -= money.value;
                ans++;
                break;
            }
            if (needToPay > 0) break;
        }
        System.out.println(ans);
    }
}


发表于 2022-09-13 02:42:08 回复(0)
感谢评论区大佬@难受啊兄弟 提供的思路,算是理解这道题的做法了。在大佬的代码上做了点修改,用了最简单最易懂的做法。
import java.util.*;
import java.io.*;

class Money{
    public int value;//钞票面额
    public int num;//该面额钞票的数量
}

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) throws IOException{
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        String[] s = in.readLine().split(" ");
        int n = Integer.parseInt(s[0]), m = Integer.parseInt(s[1]);
        Money[] a = new Money[n];
        for(int i = 0; i < n; i++){
            s = in.readLine().split(" ");
            a[i] = new Money();
            a[i].value = Integer.parseInt(s[0]);
            a[i].num = Integer.parseInt(s[1]);
        }
        Arrays.sort(a, new Comparator<Money>(){//将钞票按从大到小排列
            public int compare(Money a1, Money a2){
                return a2.value - a1.value; 
            }
        });
        int large = 0;//用于后续直接从面额小于工资的钞票开始遍历
        int month = 0;//能发工资的月份
        //将面额大于等于工资的钞票全部取出直接发工资
        while(large < n && a[large].value >= m){
            month += a[large].num;
            a[large].num = 0;
            large++;
        }
        //处理面额小于工资的钞票
        while(true){
            int npay = m;//记录一个月还需要支付多少工资
            //先从面额大的开始取,取到刚好不超过工资数量
            for(int i = large; i < n; i++){
                if(a[i].num == 0) continue;
                int pNum = npay / a[i].value;//记录某个面额钞票能够补的数量
                pNum = Math.min(pNum, a[i].num);//不能超过钞票剩余数量
                npay -= pNum * a[i].value;
                a[i].num -= pNum;
                if(npay <= 0) break;//刚好取到工资的数量,直接跳出循环
            }
            //如果刚好取到了工资的数量,就直接发工资
            if(npay <= 0){
                month++;
                continue;
            }
            //再从面额小的开始补工资
            for(int i = n-1; i >= large; i--){
                if(a[i].num == 0) continue;
                while(npay > 0 && a[i].num > 0){
                    npay -= a[i].value;
                    a[i].num--;
                }
                if(npay <= 0){//工资补够了,跳出循环
                    month++;
                    break;
                }
            }
            if(npay > 0) break;//钞票都补完了还没补够,没救了
        }
        System.out.println(month);
    }
}


发表于 2023-03-28 12:13:49 回复(0)
import java.util.*;

class Money
{
    public int mianZhi;
    public int num;
}

public class Main
{
    public static void main(String[] args)
    {
        Scanner in = new Scanner(System.in);
        int numOfMoney = in.nextInt();  // 面值的种类
        int salary = in.nextInt();      // 每月的工资
        Money[] money = new Money[numOfMoney];
        for (int i = 0; i < money.length; ++i)
        {
            money[i] = new Money();
            money[i].mianZhi = in.nextInt();
            money[i].num = in.nextInt();
        }
        Arrays.sort(money, new Comparator<Money>()
        {
            @Override
            public int compare(Money m1, Money m2)
            {
                // 降序
                return m2.mianZhi - m1.mianZhi;
            }
        });     // 对Money数组按照mianZhi降序排序

        int month = 0;

        // 因为不能找零,所以先把面值大于salary的钞票用完
        int moreThanSalary = 0;
        while (moreThanSalary < money.length && money[moreThanSalary].mianZhi >= salary)    // 防止下标越界!!!
        {
            month += money[moreThanSalary].num;
            money[moreThanSalary].num = 0;
            ++moreThanSalary;
        }

        // 然后用剩下的钞票开始组合(贪心),此时moreThanSalary就是面值小于salary的钞票的起始下标
        while (true)
        {
            int remain = salary;    // remain表示剩余的应发工资
            for (int j = moreThanSalary; j < money.length; ++j)  // 贪心一趟,结束后remain>=0
            {
                while (money[j].mianZhi <= remain && money[j].num > 0)  // 当前面值小于remain,就使劲用
                {
                    remain -= money[j].mianZhi;
                    --money[j].num;
                }
            }
            for (int j = money.length - 1; j >= moreThanSalary && remain > 0; --j)   // 若remain>0,从小面值到大面值再凑
            {
                if (money[j].num > 0)
                {
                    remain -= money[j].mianZhi;
                    --money[j].num;
                }
            }
            if (remain > 0) // remain仍然大于0,说明剩余的钞票已经不够用,结束循环
            {
                break;
            }
            else    // 多发一个月的工资
            {
                ++month;
            }
        }

        System.out.println(month);
    }
}

发表于 2023-03-27 19:55:23 回复(0)
import java.util.*;

class Money {
    int value;
    int nums;

    public Money(int value, int nums) {
        this.value = value;
        this.nums = nums;
    }
}
public class Main {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n ,m;
        n = sc.nextInt();
        m = sc.nextInt();
        List<Money> list = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            int value = sc.nextInt();
            int nums = sc.nextInt();
            Money money = new Money(value,nums);
            list.add(money);
        }
        Collections.sort(list,(o1,o2) -> o2.value- o1.value);

        int ans  = 0;
        // 第一种情况把大于工资的花完
        for (int i = 0; i < list.size(); i++) {
            //先把大钱用完

            Money money  = list.get(i);
            if (money.value < m)
                break;
            ans += money.nums;
            money.nums = 0;
        }
        
        
        while (true){
            int res = m;
            for (int i = 0; i < list.size(); i++) {
                Money tmp = list.get(i);
                if (tmp.nums == 0)
                    continue;

                int need = res / tmp.value;
                if (need > tmp.nums)
                    need = tmp.nums;

                res = res - need * tmp.value;
                tmp.nums -= need;
            }

            // 到这里所有可用的面额都大于 res ,从小开始补余额
            for (int i = list.size() -1; i >= 0 && res>0; i--) {
                Money tmp = list.get(i);
                if (tmp.nums == 0)
                    continue;

                int need = Math.max(res / tmp.value ,1 );
                need = Math.min(need, tmp.nums);

                tmp.nums = tmp.nums - need;
                res -= need * tmp.value;
            }
            if (res >0)
                break;
            ans++;
            
        }
        
//        for (int i = 0; i < list.size(); i++) {
//            System.out.println("value :"+list.get(i).value + " nums:"+list.get(i).nums);
//        }
        System.out.println(ans);
    }
}
发表于 2022-04-19 16:17:18 回复(1)
卧槽!我A了
#include <bits/stdc++.h>

typedef std::pair<int, int> PAII;
using namespace std;

struct Money {
    long long x;
    int y;

    bool operator<(Money m2) const {
        return x < m2.x;
    }
};

istream &operator>>(istream &is, Money &money) {
    is >> money.x >> money.y;
    return is;
}

int main() {
    int n;
    long long m;
    cin >> n >> m;
    long long totalSum = 0L;
    vector<Money> moneyGroup(n);
    unordered_map<int,int> modMoney;

    for (int i = 0; i < n; ++i) {
        cin >> moneyGroup[i];
        totalSum += moneyGroup[i].x * moneyGroup[i].y;
    }
    if (totalSum < m) {
        cout << 0 << endl;
    } else {

        sort(moneyGroup.begin(), moneyGroup.end());

        long long ans = 0;
        while (1) {
            long long rest = m;
            for (int i = n - 1; i >= 0 && rest > 0; --i) {
                if (moneyGroup[i].y == 0)
                    continue;
                // 有个整除关系感觉没用到。
                // 先用大钱的思路没错,但是用小钱补的时候,小钱能提供更多的容错率

                auto need = rest / moneyGroup[i].x;
                if (need > moneyGroup[i].y) {
                    need = moneyGroup[i].y - 1;
                }
                rest -= need * moneyGroup[i].x;
                moneyGroup[i].y -= need;
            }

            for (int i = 0; i < n && rest > 0; ++i) {
                if (moneyGroup[i].y == 0)
                    continue;
                auto need = max((int)(rest / moneyGroup[i].x),1);
                if (need > moneyGroup[i].y) {
                    need = moneyGroup[i].y;
                }

                rest -= need * moneyGroup[i].x;
                moneyGroup[i].y -= need;
            }
            if (rest > 0)
                break;
            ++ans;
        }
        cout << ans;
    }
}


发表于 2022-03-20 21:17:01 回复(5)