数学建模国赛C题:原材料订购与供应商评价优化实战解析
简介一套完整的2021年全国大学生数学建模竞赛C题备赛资料面向参赛选手和希望提升数学建模能力的学习者。资源内含解题思路、模型构建、算法实现及复盘过程覆盖从问题理解、模型建立到求解验证的完整链路。压缩包共27个文件包含13个MATLAB脚本、10个MAT数据文件和4个Excel表格脚本按题目分模块实现对应算法mat文件用于保存中间计算结果Excel表格则存放供应商、转运商等原始数据及订购方案结果便于二次分析。资料整体仅996KB结构清晰目前已有2303人学习。通过学习这些代码和数据读者可以还原2021年C题的实际求解流程掌握将数学理论转化为可运行程序的方法并理解数据整理与结果验证的细节对备战国赛或日常建模练习都有实在的参考价值。1. 这道题到底在考什么先别急着跑代码2021年高教社杯全国大学生数学建模竞赛C题“生产企业原材料的订购与运输”当年不知道坑了多少队伍。我见过太多团队拿到题就开始调代码结果跑出来一堆莫名其妙的结果最后连题目都没读懂。这道题表面看是数据分析题实际上是一道披着数据外衣的优化决策题核心就三个字选、订、运。先说题目业务背景用大白话讲一家生产企业需要生产产品产品需要ABC三类原材料每周需求量是2.82万立方米其中A类占50%、B类占30%、C类占20%。企业自己仓库容量有限最多存2.82万立方米。现在有402家供应商、8家转运商企业要制定未来24周的订购计划和转运计划。供应商给多少货、转运商运输损耗多少这些历史数据都给你了共240周约5年的供货数据。这意味着你要解决三个递进的问题问题一从402家供应商中挑出最重要的50家其中A类供应商至少30家然后确定每周向哪些供应商订购多少原材料。问题二在问题一基础上确定8家转运商中选几家以及如何分配转运量使得运输损耗最小化。问题三增加约束——供应商供货量尽量稳定、转运商损耗率有波动的情况下重新制定订购和转运方案。说白了这道题就是要你用数学语言把“供应商评价—订购决策—转运优化”这条业务链完整建模。评分老师看的不只是结果更是你的建模逻辑是否闭环、假设是否合理、结果是否可解释。我复盘当年的优秀论文发现拿高分的队伍几乎都有一个共同点逻辑链条非常清晰每一步都有依据而不是套个模型堆上去就完事。这道题适合谁来参考如果你是第一次参加国赛或者准备打2025年国赛想找一道练手题这道题是非常好的“优化建模入门级综合题”。它不是最难的但覆盖了数据处理、评价方法、线性规划、灵敏度分析等竞赛高频考点代码量适中吃透它能少走很多弯路。2. 整体建模思路先评价、再订购、后转运三个问题环环相扣2.1 从“供应商评价”到“订购规划”的递进逻辑很多队伍在问题一就卡住了原因是不知道“选50家供应商”和“制定订购方案”之间的关系。实际上这两个步骤是递进的先根据历史供货数据评价每一家供应商的综合表现供货能力、供货稳定性、被订购后的响应程度等给出排名然后从排名中取前50家A类至少30家是硬约束最后在选定50家的基础上建立优化模型求每周订购量。这里有个关键认知题目中的“重要供应商”不是单纯看供货量大而是看“被订购后能稳定供货”。你翻原始数据会发现有些供应商虽然供货总量不小但经常出现“你订了1000他只给800”甚至“订了却一单不发”的情况。这类供应商对你的生产计划是致命的。所以评价指标不能只看“供货能力”还要看“供货可靠性”。当年优秀论文里常见的做法是构造“企业满意度”指标——用每家供应商的实际供货量与订购量的比值供货完成率作为核心评价维度再结合供货总量、供货波动性等做多指标综合评价。我用的评价方法比较推荐熵权法 TOPSIS。熵权法根据数据本身的信息量确定指标权重避免人为打分的主观性TOPSIS根据“距离最优解最近、距离最劣解最远”来排名结果直观、好解释。这套组合在国赛里很常见评委容易认可。2.2 为什么要“两级优化”而不是“一步到位”第二个容易踩坑的地方是有人试图用一个模型同时解决“选哪几家供应商”和“每周订多少”两个问题结果模型复杂度爆炸、求解时间超长、甚至无解。我复盘后认为合理做法是两级优化第一级用“0-1整数规划”确定供应商准入名单选/不选第二级在名单确定的前提下用“线性规划”求每周最小化订购成本的订购量。为什么分开做因为如果合在一起决策变量既包含离散的“选/不选”0-1变量又包含连续的“订多少”实数变量就变成了混合整数规划MIP。MIP虽然理论上能求解但规模一大402家 × 24周 × 多材料类型求解时间非常恐怖而且对竞赛软件不友好。分开做既能跑得快又能让每个环节的结果都有业务含义答辩时好讲。从拿奖角度说模型简洁、结果可解释比模型复杂但说不清楚要好得多。2.3 转运方案本质上是一个“运输分配优化”到了问题二转运商加入模型目标变成了“在满足订购需求的前提下让转运损耗最小”。8家转运商的运输损耗率是不同的历史数据里给了每家转运商过去240周的损耗情况。你把每一家转运商的“平均损耗率”算出来就得到了一个“订购量—转运量—到货量”的换算关系。核心公式非常直白到货量 转运量 × (1 - 损耗率)由于企业每周实际收到的原材料必须满足产能需求2.82万立方米并且转运商数量有限制题目要求尽量少用转运商这就是一个典型的“带容量约束的运输问题”。你需要在8家转运商中选几家、每家分配多少量、总损耗最小。把它写成线性规划模型用Excel的Solver都能直接求更别说Python里用scipy.optimize.linprog或pulp几行代码就能解决。但这里有一个隐藏评分点到货量要与周需求匹配而不能让转运量直接等于订购量。如果忽略了损耗率你的方案会导致原材料短缺产能不足整个决策链就断了。多算一步损耗折算就能在细节处拉开差距。3. 代码架构与核心实现从数据清洗到方案输出一站到底3.1 数据预处理拿到手的第一件事不是建模是清洗原始数据表里有好几张表供应商供货数据含240周供货量、供货类型、转运商损耗数据、企业产能需求等。每张表几十万行数据第一件事是清洗和透视。这里分享一套我实际用过的、经过整理的代码架构你可以直接照着改。第一步是数据透视把“长表”变成“宽表”import pandas as pd import numpy as np # 读取原始数据sheet名称根据实际文件调整 df_supply pd.read_excel(C题附件.xlsx, sheet_name供应商供货数据) # 关键把每家企业240周的供货序列透视为一行 supply_pivot df_supply.pivot_table( index供应商ID, # 每一行是一家供应商 columns周次, # 每一列是一周 values供货量, aggfuncsum ).fillna(0) # 数据里大量缺失值其实表示“该周未供货”应当填充为0而不是删除 print(supply_pivot.shape) # 应该得到 402行 × 240列我特别强调一下fillna(0)这一步非常重要。很多队伍一看有缺失值就各种插值填补实际这些空值在业务上就是“没供货”填成0才符合业务逻辑。当年我见过有队伍用均值去填充这些0结果供应商的评价被严重扭曲利润算出来全是错的。记住一句话业务含义优先于数据统计技巧。3.2 供应商评价模块熵权法TOPSIS完整实现在做评价之前先要构造决策矩阵。常见的指标有四个供货量均值反映供应商供货能力供货量标准差/变异系数反映供货稳定性越小越好供货完成率实际供货量与被订购量的比值但原始数据里没有订购量只能近似用“供货量是否始终大于0”来替代供货连续率240周里实际供货周数占比。构造好指标矩阵后用熵权法计算权重再用TOPSIS排名。下面是核心代码from sklearn.preprocessing import MinMaxScaler def entropy_weight(X): 熵权法计算指标权重 # X为 n×m 的指标矩阵n家供应商m个指标 # 先归一化 X X / X.sum(axis0) # 计算熵值 k 1 / np.log(len(X)) entropy -k * (X * np.log(X 1e-12)).sum(axis0) # 计算权重 weight (1 - entropy) / (1 - entropy).sum() return weight def topsis_score(X, weight): TOPSIS计算综合得分 # 归一化向量归一化 norm_X X / np.sqrt((X ** 2).sum(axis0)) weighted_X norm_X * weight # 加权决策矩阵 # 正负理想解 ideal_best weighted_X.max(axis0) ideal_worst weighted_X.min(axis0) # 距离 dist_best np.sqrt(((weighted_X - ideal_best) ** 2).sum(axis1)) dist_worst np.sqrt(((weighted_X - ideal_worst) ** 2).sum(axis1)) # 贴近度 score dist_worst / (dist_best dist_worst) return score运行之后你会得到一个从高到低的供应商排名表。注意这一步不能光靠代码跑完就结束要对照题目要求选出的50家中A类材料供应商必须至少30家。所以你在筛选前先按材料类型分组A类里取前30~40名B/C类里补足到50家这样可以保证硬约束满足的同时排名靠前。3.3 订购方案线性规划求解最小化库存与订购成本选定了50家供应商后问题变成未来24周每周向这50家分别订多少货约束条件有每周到货总量不小于2.82万立方米、企业仓库存量不超过2.82万立方米、供应商原材料有类型区分且必须满足ABC比例为50%:30%:20%。建立线性规划模型时决策变量是x[i][t]表示第i家供应商第t周的订购量。约束条件为每种材料的周到货量 周需求对应比例A类1.41万m³B类0.846万m³C类0.564万m³库存在制品 本周到货 - 本周消耗 ≤ 库存上限2.82万m³各供应商有生产能力上限取历史最大供货量或题目给定的上限。用pulp求解非常方便import pulp # 构建模型 model pulp.LpProblem(Ordering_Plan, pulp.LpMinimize) # 决策变量第i家供应商第t周的订购量立方米 x pulp.LpVariable.dicts(x, [(i, t) for i in range(50) for t in range(24)], lowBound0) # 目标函数最小化总订购和存储成本 cost_order 10 # 订购单位成本可以根据题目设定 cost_inv 2 # 库存单位成本 inventory [0] * 25 # 记录每周初库存 model pulp.lpSum([x[i, t] * cost_order for i in range(50) for t in range(24)]) \ pulp.lpSum([inventory[t] * cost_inv for t in range(1, 25)]) # 核心约束每周到货量必须满足当周需求先按原料类型拆分 for t in range(24): model pulp.lpSum([x[i, t] for i in range(50) if supplier_type[i] A]) 14100 # B、C类型同理这个模型求解速度极快基本上几秒钟就能跑完。跑完后不要急着收工要做“结果合理性检查”看看有没有供应商某周订购量异常大/异常小库存曲线是否周期性波动但不上限如果结果不合理多半是约束条件写错了或者参数设置有问题回到前面排查。3.4 转运方案从“单个损耗均值”到“动态损耗矩阵”问题二引入转运商很多人简单算一个平均损耗率就开始分配这是能拿省奖但冲不了国奖的水平。更优解法是不只看平均值还要看损耗率的方差。转运商的损耗波动直接对应供应链风险。我的做法是这样的# 计算各转运商损耗率的均值与标准差 loss_df pd.read_excel(C题附件.xlsx, sheet_name转运数据) loss_summary loss_df.groupby(转运商ID)[损耗率].agg([mean, std]) print(loss_summary)有了均值之后建立转运分配模型。决策变量y[j][t]表示第j家转运商第t周负责的运输量目标是总损耗最小约束是总运输量不小于总订购量、每家转运商有最大运输能力。这个模型比订购模型还简单我直接复用pulp求解。这里要提醒一个细节转运商的“实际运输能力”需要你自己从数据里估算。原题不会直接给你而是藏在历史损耗数据里——比如某家转运商每周最大转运量有隐含上限超出后损耗率会明显增加。虽然题目没明说但你可以在论文里主动做这个约束设定并说明依据这会让评阅老师觉得你考虑周全。4. 常见问题与排查技巧这些坑我当年全踩过4.1 供应商“零供货”周导致的评价偏差这是最大的坑没有之一。原始数据中大量供应商有部分周供货量为0如果你直接用均值评价零供货多的企业均值会被拉得很低这没有错但如果你使用标准差作为稳定性指标零供货多的企业标准差反而大波动大排名就会不合理的降低。我的建议是分两个维度看——如果一家企业长期比如连续20周零供货那它本质上已经“退出”供应链了如果只是偶尔零供货说明它在备货周期中。建议把“供货连续率”单独作为指标而不是和均值混在一起算标准差。具体做法设定一个“最低供货周数”阈值比如80周以上才算有效供应商先过滤掉一批完全不合格的再对剩余供应商做精细化评价。这一步能帮你省掉大量无效计算。4.2 库存约束导致模型无解很多人跑订购模型时发现无解原因是在某些周即使所有供应商满负荷供货也不足以满足当周需求。这有两种可能一是你选的50家供应商本身“总产能不足”二是你没有利用“库存缓冲”。解决办法是引入“周初库存”作为桥梁变量把各周的独立约束关联起来。第t周的可用量 第t-1周的剩余库存 第t周到货量只要累计24周总到货量 ≥ 累计24周总需求模型更可能找到可行解。如果你一开始就要求“每周到货量每周需求量”把每条约束卡死模型很可能整体不可行。我当年就是在这个问题上卡了整整一天最后把周约束改为供需平衡约束才跑通。4.3 求解结果震荡、相邻周订购量突变线性规划有一个特点目标函数是最小化总成本如果成本参数设置不合理比如订购成本和库存成本比例失调模型会倾向于“一次性订满然后慢慢消耗库存”或“每周少量多次”这两种极端策略。结果就是订购量在相邻周大幅摆动方案不具备实际操作性。解决思路很简单给订购量的周际变化加惩罚项。在目标函数里加上λ × |x[i][t] - x[i][t-1]|λ大小自己调。加了这一项之后模型就会自动倾向“平滑订购”方案。虽然这是一个小技巧但在答辩时一解释评委反而会认可你的工程思维。4.4 问题三的“稳定性”要求怎么处理问题三要求订购方案“尽量稳定”很多队伍不知道如何量化。我的做法是把“稳定”转化为数学约束每家供应商24周内的订购量变异系数CV不超过某个阈值比如0.3。在线性规划里加入for i in range(50): mean_i pulp.lpSum([x[i, t] for t in range(24)]) / 24 for t in range(24): # 线性化近似限制每期订购量偏离均值幅度 model x[i, t] mean_i * 1.3 model x[i, t] mean_i * 0.7这里做了一点线性化近似虽然不完全等价于CV约束但在工程上完全可以接受。你还可以把0.7和1.3这两个比例改成年份折减系数做灵敏度分析看看方案对稳定性参数的敏感程度——这一步做出来就是论文里的“灵敏度分析”章节很多评委喜欢看这个。5. 实操过程与结果验证从模型到论文的完整链路5.1 全流程跑通的基本步骤回顾我再把整个操作流程以清单方式梳理一遍方便你对照执行读入数据数据清洗fillna(0)、类型转换、单位统一构造评价指标矩阵跑熵权法TOPSIS获得供应商排名按材料类型筛选出50家供应商A类不少于30家构造订购优化线性规划模型定义目标函数和约束求解根据订购方案构造转运分配模型加入损耗率求解对问题三增加“稳定性约束”并重新求解结果可视化折线图看订购趋势、柱状图看供应商贡献度、饼图看材料占比灵敏度分析调整关键参数观察结果变化趋势把以上过程整合为论文确保每张图表都有业务解释。5.2 结果可视化图表是拿分利器我见过很多队伍求解结果不错但是论文里只有一张密密麻麻的数据表评委根本看不下去。数学建模论文的图表非常重要甚至能直接决定你是省二还是省一。推荐最少做四张图24周订购量堆叠面积图展示每周各类材料订购量变化50家供应商贡献度柱状图前10家供应商的供货占比是否过高如果过高说明方案太集中、风险大库存水位折线图验证库存始终在上下限内转运商使用比例饼图展示问题二中最少使用的转运商组合。代码上直接用matplotlib三五行就能出一张图。重点是图要清晰、有标注、有结论。图片下方一定要写“由图可知……”这是竞赛论文的固定环节千万别省。5.3 代码组织与复现建议我提供的代码是一套完整流程但我不建议你直接复制粘贴跑完交差。更好的用法是先看懂每一段代码对应的模型假设再根据你自己的理解调整参数和约束。评委最反感的是“数据对不上”“模型和代码脱节”的问题。确保你论文里的公式和代码逻辑完全一致这一步能让你避开最严重的扣分项。另外强烈建议用Jupyter Notebook组织代码一段说明文字配一段代码再配一张图这样赛后复盘和论文撰写都非常顺畅。6. 我的复盘心得与扩展思路最后聊点实在的。这道题我反复做过三遍每次都有新收获。第一遍自己闷头做卡在数据清洗和模型求解上第二遍参照优秀论文逐段拆解意识到“业务理解”比“模型炫技”更重要第三遍重新写代码、优化模型终于能把整个思路从头到尾讲清楚。数学建模比赛真正锻炼人的地方不是会用多少高级算法而是把一个模糊的业务问题翻译成精确的数学模型再通过代码求解并对结果负责。这道C题恰好完美体现了这个过程。如果后续你还想深入有几个方向可以扩展一是把确定性的线性规划扩展为随机规划把供应商供货量的不确定性显式建模二是用遗传算法求解更大规模的订购方案感受启发式算法在组合优化中的应用三是加入强化学习思路把“周订购决策”建模为马尔可夫决策过程用Q-learning做一个自适应订购策略。这些扩展不仅能加深你对这个题目的理解还能迁移到供应链管理、生产排程等真实场景。我们当年做完这道题之后直接把它改成了课程设计项目拿了优秀项目奖。一套代码多次复用价值最大化。本文还有配套的精品资源点击获取

相关新闻

最新新闻

日新闻

周新闻

月新闻