C 语言贪心算法实战:计算发工资所需最少人民币张数

57 阅读7分钟

C 语言贪心算法实战:计算发工资所需最少人民币张数

贪心算法是一种 “局部最优→全局最优” 的经典算法思想,在找零、资源分配等场景中应用广泛。本文以 “计算发工资所需最少人民币张数” 为例,详解贪心算法的核心策略(优先用大面值),结合完整代码实现多组工资数据的计算,拆解算法逻辑与优化技巧,帮助掌握贪心算法的实际应用。

一、贪心算法核心思路(找零 / 发工资场景)

1. 问题需求

给定若干员工的工资金额,用 100、50、10、5、2、1 元面值的人民币发放,要求计算最少的总张数

2. 贪心策略(核心)

优先使用大面值人民币,再用小面值补足剩余金额 —— 因为大面值的 “单张价值” 更高,能最大程度减少总张数,且人民币的面值体系(100/50/10/5/2/1)满足 “贪心选择性质”,局部最优选择可推导出全局最优解。

  • 示例:工资 187 元 → 1 张 100 + 1 张 50 + 3 张 10 + 1 张 5 + 1 张 2 → 总计 7 张(若不用大面值,总张数会更多)。

3. 程序设计要点

  • 多组输入:循环读取员工人数,输入非正整数时退出;
  • 逐人计算:对每个员工的工资,按 “100→50→10→5→2→1” 顺序计算面值张数;
  • 优化逻辑:剩余工资为 0 时,提前终止小面值计算,提升效率。

二、完整代码实现与解析

c

运行

/******************************
*文件名称:Salaries_greedy.c
*作者:czy
*邮箱:caozhiyang_0613@163.com
*创建日期:2025/12/25
*修改日期:2025/12/26
*文件功能:贪心算法计算发工资所需最少人民币张数
*核心思路:
*  1. 贪心策略:优先使用大面值人民币(100→50→10→5→2→1),保证总张数最少;
*  2. 多组输入:循环读取员工人数,输入负数/0时退出程序;
*  3. 逐人计算:对每个员工的工资,依次用各面值计算所需张数,累加得到总数。
*****************************/

#include<stdio.h>

int main()
{
    // 定义人民币面值数组(按从大到小排序,贪心算法核心)
    int denomination[] = {100, 50, 10, 5, 2, 1};
    int n;          // 员工人数
    int salary;     // 单个员工的工资金额
    int total_count;// 每组数据所需的总人民币张数
    
    // 程序说明提示
    printf("===== 贪心算法计算最少人民币张数 =====\n");
    printf("提示:输入员工人数(正整数),输入负数/0退出程序\n");
    
    // 循环读取多组测试数据(输入n<=0时退出)
    while(1)
    {
        printf("\n请输入要发工资的人数:\n");
        scanf("%d", &n);
        
        // 终止条件:输入非正整数,退出循环
        if(n <= 0)
        {
            printf("程序退出!\n");
            break;
        }
        
        // 初始化总张数为0(每组数据独立计算)
        total_count = 0;
        
        // 提示用户输入每个员工的工资
        printf("请依次输入%d个员工的工资金额(正整数):\n", n);
        for(int i = 0; i < n; i++)
        {
            scanf("%d", &salary);
            
            // 贪心核心:依次用大面值计算所需张数
            for(int j = 0; j < 6; j++) // 6种面值(100/50/10/5/2/1)
            {
                // 累加当前面值的张数(工资 ÷ 面值 = 该面值需要的张数)
                total_count += salary / denomination[j];
                // 更新剩余工资(工资 % 面值 = 扣除当前面值后剩余的工资)
                salary = salary % denomination[j];
                
                // 优化:剩余工资为0时,无需继续计算小面值
                if(salary == 0)
                {
                    break;
                }
            }
        }
        
        // 输出每组数据的结果
        printf("本次发放工资所需的最少人民币张数为:%d\n", total_count);
    }

    return 0;
}

1. 核心变量与数组解析

变量 / 数组名类型功能
denominationint 数组存储人民币面值,按从大到小排序(贪心算法的关键)
nint每组输入的员工人数
salaryint单个员工的工资金额,逐次扣除大面值后更新
total_countint每组数据的总张数,逐人累加

2. 多组输入循环(while (1))

c

运行

while(1)
{
    scanf("%d", &n);
    if(n <= 0)
    {
        printf("程序退出!\n");
        break;
    }
    // 后续计算逻辑
}
  • 无限循环while(1)实现多组数据输入;
  • 终止条件:输入员工人数n≤0时,执行break退出循环,程序结束;
  • 每组数据独立计算,避免前一组结果干扰。

3. 贪心算法核心逻辑(双层 for 循环)

c

运行

for(int i = 0; i < n; i++) // 遍历每个员工
{
    scanf("%d", &salary);
    for(int j = 0; j < 6; j++) // 遍历每种面值(从大到小)
    {
        total_count += salary / denomination[j]; // 累加当前面值张数
        salary = salary % denomination[j]; // 更新剩余工资
        if(salary == 0) break; // 剩余工资为0,提前终止
    }
}
关键步骤拆解(以工资 187 元为例):
  1. j=0(面值 100):187/100=1(1 张 100),total_count+1;剩余工资187%100=87
  2. j=1(面值 50):87/50=1(1 张 50),total_count+1;剩余工资87%50=37
  3. j=2(面值 10):37/10=3(3 张 10),total_count+3;剩余工资37%10=7
  4. j=3(面值 5):7/5=1(1 张 5),total_count+1;剩余工资7%5=2
  5. j=4(面值 2):2/2=1(1 张 2),total_count+1;剩余工资2%2=0
  6. 剩余工资为 0,执行break,无需计算面值 1,最终总张数 = 1+1+3+1+1=7。
优化点:

if(salary == 0) break;:剩余工资为 0 时,直接跳出面值循环,避免无效的小面值计算(如工资 200 元,计算完 100 面值后剩余 0,无需再算 50/10 等)。

三、运行结果示例

示例 1:正常输入(2 名员工,工资 187、258)

plaintext

===== 贪心算法计算最少人民币张数 =====
提示:输入员工人数(正整数),输入负数/0退出程序

请输入要发工资的人数:
2
请依次输入2个员工的工资金额(正整数):
187
258
本次发放工资所需的最少人民币张数为:15
计算验证:
  • 187 元:100×1 + 50×1 + 10×3 + 5×1 + 2×1 = 7 张;
  • 258 元:100×2 + 50×1 + 5×1 + 2×1 + 1×1 = 8 张;
  • 总计:7+8=15 张,与程序输出一致。

示例 2:输入非正整数(退出程序)

plaintext

请输入要发工资的人数:
0
程序退出!

四、贪心算法的适用条件与扩展

1. 适用条件

贪心算法能得到最优解的核心是 “贪心选择性质”+“最优子结构”:

  • 贪心选择性质:每一步的局部最优选择(选最大面值)能推导出全局最优;
  • 最优子结构:子问题的最优解能组合成原问题的最优解。

注意:若面值体系不满足该性质(如面值为 1、3、4,找零 6 元),贪心算法会得到次优解(4+1+1=3 张),而最优解是 3+3=2 张,此时需用动态规划。

2. 扩展优化思路

(1)工资合法性校验

新增工资输入校验,避免负数 / 0 工资导致计算错误:

c

运行

scanf("%d", &salary);
if(salary <= 0)
{
    printf("错误:工资金额必须为正整数!\n");
    i--; // 重新输入当前员工工资
    continue;
}
(2)输出每张面值的使用数量

新增数组存储各面值的张数,输出更详细的分配结果:

c

运行

int count[6] = {0}; // 存储100/50/10/5/2/1的张数
// 替换贪心核心逻辑
for(int j = 0; j < 6; j++)
{
    count[j] = salary / denomination[j];
    total_count += count[j];
    salary = salary % denomination[j];
    if(salary == 0) break;
}
// 输出面值明细
printf("100元:%d张,50元:%d张,10元:%d张,5元:%d张,2元:%d张,1元:%d张\n",
       count[0], count[1], count[2], count[3], count[4], count[5]);

五、新手避坑指南

  1. 面值数组顺序错误:若面值数组按从小到大排序(1→2→5→10→50→100),会得到最多张数,违背贪心策略;
  2. 总张数未初始化:每组数据需将total_count重置为 0,否则会累加前一组结果;
  3. 忽略剩余工资优化:未加if(salary==0) break;会多执行小面值循环,虽不影响结果,但降低效率;
  4. 输入校验缺失:未校验工资 / 人数的合法性,可能导致负数参与计算,结果异常。