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. 核心变量与数组解析
| 变量 / 数组名 | 类型 | 功能 |
|---|---|---|
denomination | int 数组 | 存储人民币面值,按从大到小排序(贪心算法的关键) |
n | int | 每组输入的员工人数 |
salary | int | 单个员工的工资金额,逐次扣除大面值后更新 |
total_count | int | 每组数据的总张数,逐人累加 |
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 元为例):
j=0(面值 100):187/100=1(1 张 100),total_count+1;剩余工资187%100=87;j=1(面值 50):87/50=1(1 张 50),total_count+1;剩余工资87%50=37;j=2(面值 10):37/10=3(3 张 10),total_count+3;剩余工资37%10=7;j=3(面值 5):7/5=1(1 张 5),total_count+1;剩余工资7%5=2;j=4(面值 2):2/2=1(1 张 2),total_count+1;剩余工资2%2=0;- 剩余工资为 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→2→5→10→50→100),会得到最多张数,违背贪心策略;
- 总张数未初始化:每组数据需将
total_count重置为 0,否则会累加前一组结果; - 忽略剩余工资优化:未加
if(salary==0) break;会多执行小面值循环,虽不影响结果,但降低效率; - 输入校验缺失:未校验工资 / 人数的合法性,可能导致负数参与计算,结果异常。