目录
[1.1 问题描述](#1.1 问题描述)
[1.2 数据需求](#1.2 数据需求)
[1.3 功能需求](#1.3 功能需求)
[1.4 开发环境](#1.4 开发环境)
[2.1 抽象数据类型 ADT 的定义](#2.1 抽象数据类型 ADT 的定义)
[2.2 系统的主要功能模块](#2.2 系统的主要功能模块)
[2.3 功能模块联系图](#2.3 功能模块联系图)
[3.1 数据结构设计](#3.1 数据结构设计)
[3.2 主要算法](#3.2 主要算法)
[1. 用户界面](#1. 用户界面)
[2. 程序运行样例](#2. 程序运行样例)
[3. 程序结果分析](#3. 程序结果分析)
一、需求分析
1.1 问题描述
某工厂生产的产品需历经 3 道工序,为提升效率,需确定加工顺序,以实现生产 n 件产品的总时长最短。每件产品的各工序耗时各异。要求输入 n(n≥10),并递增 n 至少给出 3 组运行数据,对比程序运行时间。设计程序接收订单信息及工序时长,据此计算最短生产时间,即完成所有产品所需的最小时间。程序核心任务是依据输入优化工序安排,使总生产时间最小化。
1.2 数据需求
- 输入:订单信息:接收产品数量 n,代表工厂要生产的产品总数。
- 输入:工序时长信息:以二维矩阵或数组呈现,行对应产品,列对应工序,元素表示相应工序的时长。
1.3 功能需求
- 错误情况处理:输入订单及工序时长后,判断输入是否无效。若出现无效产品数量、负数工序时长等错误输入,进行错误提示或异常处理。
- 优化加工时长:运用分支限界法,根据输入的产品数量及各产品工序耗时,计算生产 n 件产品的最小总时长。
- 确定输出:依据输入,程序返回最小总时长,即完成 n 件产品所需的最短时间。
- 确定最优加工顺序:借助动态规划算法结果,确定使总时长最小的工序顺序。
1.4 开发环境
采用 Code::Blocks 作为开发环境,它是用于 C 和 C++ 等编程语言的集成开发环境(IDE),支持多种编译器,具备强大调试功能,可在开发中定位代码错误,还能将相关文件组织于项目中,便于代码管理、版本控制与共享,提高开发效率。
二、概要设计
2.1 抽象数据类型 ADT 的定义
ADT 结构体
-
- 数据对象:包含多种类型数据项,如变量、数组、指针等。
-
- 数据关系:数据项间存在相关性。
-
- 基本操作
-
-
- 创建:使用 typedef struct Node 创建新结构体对象。
-
-
-
- 访问:通过结构体对象和成员名访问成员。
-
-
-
- 赋值:将一个结构体对象赋值给当前对象。
-
-
-
- 修改:修改结构体中成员的值。
-
ADT info
-
- 数据对象:信息记录,含整型成员和数组成员。
-
- 数据关系:无特定关系描述。
-
- 基本操作:无特定操作描述。
ADT 线性表
-
- 数据对象:相同数据类型数据元素的集合。
-
- 数据关系:数据元素间有前驱后继关系。
-
- 基本操作
-
-
- 创建:构造空顺序表 L。
-
-
-
- 插入:向线性表 L 中插入元素 e。
-
-
-
- 删除:获取并删除线性表 L 的首个元素。
-
ADT 堆
-
- 数据对象:包含元素实际数据及在堆中的位置信息。
-
- 数据关系:对于非叶子节点 i,满足 data [i] >= data [2 * i + 1] 和 data [i] >= data [2 * i + 2](或 data [i] <= data [2 * i + 1] 和 data [i] <= data [2 * i + 2]);对于节点 i 和 j,当 i 是 j 的祖先节点时,data [j] <= data [i](或 data [j] >= data [i])。
-
- 初始条件:给定顺序表 L,起始位置 s 和结束位置 m。
-
- 操作结果:在给定范围内对顺序表 L 进行操作。
2.2 系统的主要功能模块
- inlIst 函数:初始化顺序表 L 为空。
- linsert 函数:向顺序表 L 插入元素。
- GetElem 函数:获取并删除顺序表 L 的首个元素。
- heaps 函数:实现堆排序中的堆调整操作。
- Creat 函数:构建大根堆。
- Sort 函数:对数组进行堆排序。
- Min1 函数:计算指定节点 k 与其他节点权重的最小值。
- Min2 函数:计算指定节点 k 与其他节点路径权重之和的最小值。
- fenzhi 函数:使用分支限界法求解最优路径问题。
- main 函数:程序入口,接收用户输入产品数量和加工时间,调用 fenzhi 函数求解最优路径。
- 异常处理模块:判断输入是否无效,对无效输入(如无效产品数量、负数工序时长)进行错误处理并给出提示。
2.3 功能模块联系图

三、详细设计
3.1 数据结构设计
- 结构体
cpp
typedef struct {
int x;
int Lw[100] = {0};
int M[100] = {0};
int g;
} info;
线性表
cpp
typedef struct {
info data[MAXSIZE];
int length;
} Sqlist;
堆
cpp
void heaps(Sqlist &L, int s, int m) { //堆的创建
int j;
info rc;
memcpy(&rc, &L.data[s], sizeof(info));
for (j = 2 * s; j <= m; j = j * 2) {
if (j < m && L.data[j].x < L.data[j + 1].x)
++j;
if (rc.x >= L.data[j].x)
break;
memcpy(&L.data[s], &L.data[j], sizeof(info));
s = j;
}
memcpy(&L.data[s], &rc, sizeof(info));
}
void Creat(Sqlist &L) { //对堆进行创建和调整
int n, i;
n = (L.length);
for (i = n / 2; i > 0; --i)
heaps(L, i, n);
}
void Sort(Sqlist &L) { //是对 L 中的数据进行排序。
int i;
info X;
Creat(L);
for (i = L.length; i > 1; --i) {
memcpy(&X, &L.data[1], sizeof(info));
memcpy(&L.data[1], &L.data[i], sizeof(info));
memcpy(&L.data[i], &X, sizeof(info));
heaps(L, 1, i - 1);
}
}
3.2 主要算法
- Min1 函数
cpp
int Min1(int k, int n, int M[100]) {
int j, min = 10000;
for (j = 1; j <= n; j++) {
if (M[j] == 0 && j != k && min > f[j][3])
min = f[j][3];
}
if (min == 10000)
min = f[k][3];
return min;
}
该函数用于获取未被处理且工序时间最短的产品的工序时间,辅助求解最优路径。通过不断调用可在决策节点选择最优路径。
- Min2 函数
cpp
int Min2(int k, int n, info p) {
int j;
int db, sumf = 0;
for (j = 1; j <= n; j++) {
if (p.M[j] == 0)
sumf = sumf + f[j][2];
}
db = (max(Sum(1, p.g + 1, k, p), Sum(2, p.g, k, p)) + sumf + Min1(k, n, p.M));
return db;
}
该函数用于计算当前节点的最小值,辅助求解最优路径。通过不断调用可在决策节点选择最优路径。
- 分支限界法求解最优路径
cpp
void fenzhi(int n, Sqlist &L) {
info p, pl, e;
int down = 10000, up = 100000;
int i, j, x, db, sumf = 0, min3 = 100000;
for (i = 1; i <= n; i++) {
for (j = 1; j <= n; j++) {
sumf = sumf + f[j][2];
}
for (j = 1; j <= n; j++) {
if (j != i && min3 > f[j][3])
min3 = f[j][3];
}
db = f[i][1] + sumf + min3;
if (down > db) {
down = db;
x = i;
}
}
e.x = down;
e.M[x] = 1;
e.Lw[1] = x;
e.g = 1;
linsert(L, e);
while (L.length != 0) {
p = GetElem(L);
if (p.Lw[n] != 0) {
e = GetElem(L);
printf("所需最短时间是:%d\n路径是: ", p.x);
for (i = 1; i <= n; i++) {
printf("%d ", p.Lw[i]);
}
} else {
for (i = 1; i <= n; i++) {
pl = p;
if (p.M[i] == 0) {
pl.Lw[p.g + 1] = i;
pl.M[i] = 1;
pl.g = p.g + 1;
pl.x = Min2(i, n, p);
if (pl.x < up) {
linsert(L, pl);
Sort(L);
if (pl.g == n) {
up = pl.x;
deleted(down, up, L);
}
}
}
}
}
}
}
该函数实现分支限界法算法框架,通过生成新路径节点并选择最优路径,最终找到最优路径,输出最短时间和路径。
四、系统运行及结果分析
1. 用户界面

2. 程序运行样例



3. 程序结果分析
对比运行样例输出与手动计算结果,结果相似。同时记录不同 n 值下程序的运行时间,分析随着 n 增大,程序运行时间的变化趋势。若运行时间增长过快,可能需进一步优化算法或数据结构。
五、总结与思考
完成此作业过程中,深入学习了分支限界法及其应用。分支限界法是求解最优化问题的常用算法,通过搜索和剪枝高效寻找最优解。在此问题中,定义数据结构和函数,利用分支限界法搜索解空间,剪去不必要路径找到最优解。编写代码和运行程序过程中,深刻理解了分支限界法思想和实现。发现合理的排序和剪枝策略可大幅减少搜索空间、提高算法效率,且合理设计数据结构和算法能使代码更清晰易理解。此外,认识到编程中错误处理和输入验证的重要性,添加对无效输入的处理避免程序出错。总之,通过解决此问题,掌握了分支限界法应用,加深了对算法设计和错误处理的认识,为今后学习和工作奠定了基础。