#include <stdio.h> // 标准输入输出库
#include <stdlib.h> // 标准库函数,包含内存分配、系统命令等
#include <time.h> // 时间处理函数
#include <math.h> // 数学函数
#include <string.h> // 字符串处理函数
#include <ctype.h> // 字符处理函数
#define MAX_WINDOWS 10 // 最大窗口数量
#define VIP_WINDOW 0 // VIP窗口编号
#define VIP_NUMBER_BASE 1000 // VIP号码起始值
#define COMMON_NUMBER_BASE 2000 // 普通号码起始值
#define CLEAR_SCREEN system("cls") // 清屏命令
// 客户评价枚举类型
typedef enum { POOR=1, FAIR, GOOD, EXCELLENT } Rating;
// 客户结构体
typedef struct {
int number; // 客户号码
int isVip; // 是否是VIP客户
Rating rating; // 服务评价
time_t startTime, endTime; // 服务开始和结束时间
double duration; // 服务持续时间
} Customer;
// 队列节点结构体
typedef struct queue {
Customer customer; // 客户数据
struct queue* next; // 指向下一个节点的指针
} Queue, *QueuePtr;
// 链式队列结构体
typedef struct {
Queue *front, *tail; // 队列头和尾指针
} LinkQueue;
// 银行窗口结构体
typedef struct {
int windowId; // 窗口ID
int isOpen; // 窗口是否开放
int servedCount; // 已服务客户数量
Customer serving; // 当前服务的客户
float avgRating; // 平均评分
double totalServiceTime; // 总服务时间
} BankWindow;
// 银行系统结构体
typedef struct {
LinkQueue commonQueue, vipQueue; // 普通和VIP队列
BankWindow windows[MAX_WINDOWS]; // 窗口数组
int windowCount; // 窗口总数
int nextVipNumber; // 下一个VIP号码
int nextCommonNumber; // 下一个普通号码
int isTakingNumbers; // 是否正在取号
} BankSystem;
// 函数声明部分
int isEmptyQueue(const LinkQueue *Q); // 检查队列是否为空
void printDuration(double sec); // 打印时间间隔
void initBankSystem(BankSystem *bank, int windowCount); // 初始化银行系统
int enterQueue(BankSystem *bank, int isVip); // 客户进入队列
int deQueue(BankSystem *bank, int windowId); // 客户离开队列
int assignCustomerToWindow(BankSystem *bank); // 分配客户到窗口
int queryWaitingCount(const BankSystem *bank, int number); // 查询等待人数
void printStatus(const BankSystem *bank); // 打印系统状态
void printStats(const BankSystem *bank); // 打印统计信息
void adminMenu(BankSystem *bank); // 管理员菜单
void customerTakeNumber(BankSystem *bank); // 客户取号
void queryQueueStatus(BankSystem *bank); // 查询队列状态
void destroyBankSystem(BankSystem *bank); // 销毁银行系统
int getIntInput(const char *prompt, int min, int max); // 获取整数输入
void pressAnyKeyToContinue(); // 按任意键继续
// 检查队列是否为空
int isEmptyQueue(const LinkQueue *Q) {
return Q->front == Q->tail; // 头尾指针相等表示队列为空
}
// 打印时间间隔(秒转换为易读格式)
void printDuration(double sec) {
if (sec < 60) printf("%.0f秒", sec); // 小于1分钟显示秒
else if (sec < 3600) printf("%.0f分%.0f秒", sec/60, fmod(sec,60)); // 小于1小时显示分秒
else printf("%d小时%.0f分%.0f秒", (int)sec/3600, fmod(sec,3600)/60, fmod(sec,60)); // 大于1小时显示时分秒
}
// 初始化银行系统
void initBankSystem(BankSystem *bank, int windowCount) {
bank->windowCount = windowCount < MAX_WINDOWS ? windowCount : MAX_WINDOWS; // 设置窗口数量
bank->nextVipNumber = 1; // VIP号码从1开始
bank->nextCommonNumber = 1; // 普通号码从1开始
bank->isTakingNumbers = 1; // 默认允许取号
// 初始化普通队列
bank->commonQueue.front = bank->commonQueue.tail = (QueuePtr)malloc(sizeof(Queue));
bank->commonQueue.front->next = NULL;
// 初始化VIP队列
bank->vipQueue.front = bank->vipQueue.tail = (QueuePtr)malloc(sizeof(Queue));
bank->vipQueue.front->next = NULL;
// 初始化每个窗口
for (int i = 0; i < bank->windowCount; i++) {
bank->windows[i] = (BankWindow){
.windowId = i, // 窗口ID
.isOpen = 1, // 默认开放
.servedCount = 0, // 服务计数清零
.serving = { .number = -1 }, // 初始无客户
.avgRating = 0.0, // 平均评分初始0
.totalServiceTime = 0.0 // 总服务时间初始0
};
}
}
// 客户进入队列
int enterQueue(BankSystem *bank, int isVip) {
if (!bank->isTakingNumbers) { // 检查是否允许取号
printf("当前已停止取号\n");
return 0;
}
QueuePtr newNode = (QueuePtr)malloc(sizeof(Queue)); // 创建新节点
if (!newNode) { // 内存分配失败处理
printf("内存分配失败\n");
return 0;
}
// 分配号码: VIP从1000开始,普通从2000开始
newNode->customer.number = isVip ?
(VIP_NUMBER_BASE + bank->nextVipNumber++) :
(COMMON_NUMBER_BASE + bank->nextCommonNumber++);
newNode->customer.isVip = isVip; // 设置客户类型
newNode->customer.rating = 0; // 初始评价为0
newNode->next = NULL; // 新节点next指针置空
LinkQueue *q = isVip ? &bank->vipQueue : &bank->commonQueue; // 选择队列
q->tail->next = newNode; // 将新节点加入队列尾部
q->tail = newNode; // 更新尾指针
printf(isVip ? "VIP客户 %d 已进入VIP队列\n" : "客户 %d 已进入普通队列\n",
newNode->customer.number);
return 1;
}
// 客户离开队列(完成服务)
int deQueue(BankSystem *bank, int windowId) {
if (windowId < 0 || windowId >= bank->windowCount) { // 检查窗口ID有效性
printf("无效窗口编号\n");
return 0;
}
if (bank->windows[windowId].serving.number == -1) { // 检查窗口是否有客户
printf("窗口 %d 没有客户在服务\n", windowId);
return 0;
}
Customer *c = &bank->windows[windowId].serving; // 获取当前客户
c->endTime = time(NULL); // 记录结束时间
c->duration = difftime(c->endTime, c->startTime); // 计算服务时长
// 获取评价
printf("\n请对窗口 %d 的服务进行评价(1-4):\n", windowId);
printf("1. 差评\n2. 一般\n3. 好评\n4. 非常满意\n");
printf("请输入评价: ");
int rating;
while (1) { // 输入验证循环
if (scanf("%d", &rating) != 1 || rating < 1 || rating > 4) {
printf("无效输入,请输入1-4的数字: ");
while (getchar() != '\n'); // 清空输入缓冲区
continue;
}
break;
}
c->rating = rating; // 记录评价
BankWindow *w = &bank->windows[windowId]; // 获取窗口指针
// 更新平均评分
w->avgRating = (w->avgRating * w->servedCount + c->rating) / (w->servedCount + 1);
w->totalServiceTime += c->duration; // 累加服务时间
w->servedCount++; // 增加服务计数
printf("\n窗口 %d: 客户 %d 已离开,服务时长: ", windowId, c->number);
printDuration(c->duration); // 打印服务时长
printf(",服务评价: ");
const char *ratings[] = {"", "差评", "一般", "好评", "非常满意"};
printf("%s\n", ratings[c->rating]); // 打印评价文字
c->number = -1; // 标记窗口为空闲
pressAnyKeyToContinue();
return 1;
}
// 分配客户到窗口
int assignCustomerToWindow(BankSystem *bank) {
// 优先处理VIP窗口
if (bank->windows[VIP_WINDOW].isOpen && // VIP窗口开放
bank->windows[VIP_WINDOW].serving.number == -1 && // VIP窗口空闲
!isEmptyQueue(&bank->vipQueue)) { // VIP队列不为空
QueuePtr cust = bank->vipQueue.front->next; // 获取队列第一个VIP客户
bank->windows[VIP_WINDOW].serving = cust->customer; // 分配给VIP窗口
bank->windows[VIP_WINDOW].serving.startTime = time(NULL); // 记录开始时间
bank->vipQueue.front->next = cust->next; // 从队列中移除
if (bank->vipQueue.tail == cust) { // 如果是最后一个客户
bank->vipQueue.tail = bank->vipQueue.front; // 重置尾指针
}
printf("VIP客户 %d 已分配到VIP窗口 %d\n",
bank->windows[VIP_WINDOW].serving.number, VIP_WINDOW);
free(cust); // 释放节点内存
return 1;
}
// 处理其他窗口
for (int i = 0; i < bank->windowCount; i++) {
if (i == VIP_WINDOW) continue; // 跳过VIP窗口
BankWindow *w = &bank->windows[i]; // 获取窗口指针
if (w->isOpen && w->serving.number == -1) { // 窗口开放且空闲
// 优先分配VIP客户
if (!isEmptyQueue(&bank->vipQueue)) {
QueuePtr cust = bank->vipQueue.front->next;
w->serving = cust->customer;
w->serving.startTime = time(NULL);
bank->vipQueue.front->next = cust->next;
if (bank->vipQueue.tail == cust) {
bank->vipQueue.tail = bank->vipQueue.front;
}
printf("VIP客户 %d 已分配到窗口 %d\n", w->serving.number, i);
free(cust);
return 1;
}
// 分配普通客户
else if (!isEmptyQueue(&bank->commonQueue)) {
QueuePtr cust = bank->commonQueue.front->next;
w->serving = cust->customer;
w->serving.startTime = time(NULL);
bank->commonQueue.front->next = cust->next;
if (bank->commonQueue.tail == cust) {
bank->commonQueue.tail = bank->commonQueue.front;
}
printf("客户 %d 已分配到窗口 %d\n", w->serving.number, i);
free(cust);
return 1;
}
}
}
return 0; // 没有可分配的客户
}
// 查询等待人数
int queryWaitingCount(const BankSystem *bank, int number) {
// 判断客户类型
int isVip = (number >= VIP_NUMBER_BASE && number < COMMON_NUMBER_BASE);
if (isVip) { // VIP客户查询
int count = 0;
for (QueuePtr p = bank->vipQueue.front->next; p; p = p->next, count++) {
if (p->customer.number == number) return count; // 返回前面的人数
}
} else { // 普通客户查询
int count = 0;
for (QueuePtr p = bank->commonQueue.front->next; p; p = p->next, count++) {
if (p->customer.number == number) {
// 普通客户前面还有所有VIP客户
int vipCount = 0;
for (QueuePtr vip = bank->vipQueue.front->next; vip; vip = vip->next) {
vipCount++;
}
return count + vipCount; // 返回VIP队列人数+普通队列前面人数
}
}
}
// 检查是否正在服务
for (int i = 0; i < bank->windowCount; i++) {
if (bank->windows[i].serving.number == number) return 0; // 正在服务,等待人数为0
}
return -1; // 未找到该号码
}
// 打印系统状态
void printStatus(const BankSystem *bank) {
CLEAR_SCREEN; // 清屏
printf("\n==================== 银行排队系统 ====================\n");
// 当前排队情况
printf("\n【当前排队情况】\n");
printf("+------------+---------------------+\n");
printf("| 队列类型 | 排队号码 |\n");
printf("+------------+---------------------+\n");
printf("| VIP队列 | ");
for (QueuePtr p = bank->vipQueue.front->next; p; p = p->next) {
printf("%d ", p->customer.number); // 打印VIP队列号码
}
printf("\n| 普通队列 | ");
for (QueuePtr p = bank->commonQueue.front->next; p; p = p->next) {
printf("%d ", p->customer.number); // 打印普通队列号码
}
printf("\n+------------+---------------------+\n");
// 窗口服务状态
printf("\n【窗口服务状态】(窗口%d为VIP专用窗口)\n", VIP_WINDOW);
printf("+--------+--------+--------+------------+----------------+------------+----------------+\n");
printf("| 窗口ID | 状态 | 客户号 | 客户类型 | 已服务时间 | 平均评分 | 平均服务时间 |\n");
printf("+--------+--------+--------+------------+----------------+------------+----------------+\n");
for (int i = 0; i < bank->windowCount; i++) { // 遍历所有窗口
BankWindow *w = &bank->windows[i];
printf("| %-6d | %-6s | ", w->windowId, w->isOpen ? "开放" : "关闭");
if (w->serving.number == -1) { // 窗口空闲
printf("%-6s | %-10s | %-12s | %-10.1f | %-14s |\n",
"-", i == VIP_WINDOW ? "VIP专用" : "-", "-", w->avgRating, "-");
} else { // 窗口有客户
printf("%-6d | %-10s | ", w->serving.number,
(w->serving.number >= VIP_NUMBER_BASE && w->serving.number < COMMON_NUMBER_BASE) ? "VIP" : "普通");
char timeStr[20] = "-";
if (w->serving.startTime) { // 计算已服务时间
sprintf(timeStr, "%.0f秒", difftime(time(NULL), w->serving.startTime));
}
char avgTimeStr[20] = "-";
if (w->servedCount) { // 计算平均服务时间
sprintf(avgTimeStr, "%.0f秒", w->totalServiceTime / w->servedCount);
}
printf("%-14s | %-10.1f | %-14s |\n",
timeStr, w->avgRating, avgTimeStr);
}
}
printf("+--------+--------+--------+------------+----------------+------------+----------------+\n");
}
// 打印统计信息
void printStats(const BankSystem *bank) {
printf("\n【系统统计信息】\n");
printf("+------------------+--------+\n");
printf("| 项目 | 数值 |\n");
printf("+------------------+--------+\n");
printf("| 已发放VIP号码 | %-6d |\n", bank->nextVipNumber - 1);
printf("| 已发放普通号码 | %-6d |\n", bank->nextCommonNumber - 1);
int totalServed = 0, waiting = 0;
double totalTime = 0;
for (int i = 0; i < bank->windowCount; i++) { // 计算总服务人数和时间
totalServed += bank->windows[i].servedCount;
totalTime += bank->windows[i].totalServiceTime;
}
// 计算等待人数
for (QueuePtr p = bank->commonQueue.front->next; p; p = p->next) waiting++;
for (QueuePtr p = bank->vipQueue.front->next; p; p = p->next) waiting++;
printf("| 已服务客户 | %-6d |\n", totalServed);
printf("| 等待中客户 | %-6d |\n", waiting);
if (totalServed) { // 如果有服务过的客户
char avgTime[20];
sprintf(avgTime, "%.0f秒", totalTime / totalServed); // 计算平均服务时间
printf("| 平均服务时间 | %-6s |\n", avgTime);
// 计算平均评分
float totalRating = 0;
for (int i = 0; i < bank->windowCount; i++) {
totalRating += bank->windows[i].avgRating * bank->windows[i].servedCount;
}
printf("| 平均服务评分 | %-6.1f |\n", totalRating / totalServed);
}
printf("+------------------+--------+\n");
}
// 管理员菜单
void adminMenu(BankSystem *bank) {
while (1) {
CLEAR_SCREEN;
printStatus(bank); // 显示当前状态
printf("\n【管理员菜单】\n");
printf("1. 开放窗口\n");
printf("2. 关闭窗口\n");
printf("3. 开始取号\n");
printf("4. 停止取号\n");
printf("5. 查看统计信息\n");
printf("0. 返回主菜单\n");
printf("请选择操作: ");
int choice = getIntInput("", 0, 5); // 获取选择
switch (choice) {
case 1: { // 开放窗口
printf("输入要开放的窗口号(0-%d): ", bank->windowCount-1);
int n = getIntInput("", 0, bank->windowCount-1);
bank->windows[n].isOpen = 1; // 设置窗口为开放
printf("窗口 %d 已开放\n", n);
assignCustomerToWindow(bank); // 尝试分配客户
break;
}
case 2: { // 关闭窗口
printf("输入要关闭的窗口号(0-%d): ", bank->windowCount-1);
int n = getIntInput("", 0, bank->windowCount-1);
if (bank->windows[n].serving.number != -1) { // 检查窗口是否有客户
printf("窗口 %d 正在服务客户,请先完成服务\n", n);
} else {
bank->windows[n].isOpen = 0; // 设置窗口为关闭
printf("窗口 %d 已关闭\n", n);
}
break;
}
case 3: // 开始取号
bank->isTakingNumbers = 1;
printf("已开始取号\n");
break;
case 4: // 停止取号
bank->isTakingNumbers = 0;
printf("已停止取号\n");
break;
case 5: // 查看统计信息
printStats(bank);
pressAnyKeyToContinue();
break;
case 0: // 返回主菜单
return;
}
pressAnyKeyToContinue();
}
}
// 客户取号
void customerTakeNumber(BankSystem *bank) {
printf("\n【客户取号】\n");
printf("1. 普通客户取号\n");
printf("2. VIP客户取号\n");
printf("0. 返回\n");
printf("请选择: ");
int choice = getIntInput("", 0, 2); // 获取选择
if (choice == 1 || choice == 2) {
enterQueue(bank, choice-1); // 进入队列
assignCustomerToWindow(bank); // 尝试分配窗口
}
pressAnyKeyToContinue();
}
// 查询队列状态
void queryQueueStatus(BankSystem *bank) {
CLEAR_SCREEN;
printStatus(bank); // 显示当前状态
printf("\n要查询特定号码的等待人数吗? (1:是, 0:否): ");
int choice = getIntInput("", 0, 1); // 获取选择
if (choice == 1) {
printf("输入您的号码: ");
int number;
scanf("%d", &number); // 获取号码
int cnt = queryWaitingCount(bank, number); // 查询等待人数
if (cnt >= 0) {
printf("号码 %d 前面还有 %d 人\n", number, cnt);
} else {
printf("号码 %d 未找到\n", number);
}
pressAnyKeyToContinue();
}
}
// 销毁银行系统(释放内存)
void destroyBankSystem(BankSystem *bank) {
// 释放普通队列内存
QueuePtr p;
while (bank->commonQueue.front) {
p = bank->commonQueue.front;
bank->commonQueue.front = p->next;
free(p);
}
// 释放VIP队列内存
while (bank->vipQueue.front) {
p = bank->vipQueue.front;
bank->vipQueue.front = p->next;
free(p);
}
}
// 获取整数输入(带验证)
int getIntInput(const char *prompt, int min, int max) {
int value;
while (1) {
printf("%s", prompt); // 显示提示
if (scanf("%d", &value) != 1) { // 读取输入
printf("输入无效,请输入数字\n");
while (getchar() != '\n'); // 清空输入缓冲区
continue;
}
if (value >= min && value <= max) { // 检查范围
return value;
}
printf("输入必须在%d到%d之间\n", min, max);
}
}
// 按任意键继续
void pressAnyKeyToContinue() {
printf("\n按任意键继续...");
while (getchar() != '\n'); // 清空输入缓冲区
getchar(); // 等待用户按键
}
// 主函数
int main() {
BankSystem bank; // 创建银行系统实例
printf("请输入银行窗口数量(1-%d): ", MAX_WINDOWS);
int windowCount = getIntInput("", 1, MAX_WINDOWS); // 获取窗口数量
initBankSystem(&bank, windowCount); // 初始化系统
while (1) { // 主循环
CLEAR_SCREEN;
printStatus(&bank); // 显示状态
printf("\n【主菜单】\n");
printf("1. 客户取号\n");
printf("2. 客户离开(完成服务)\n");
printf("3. 查询排队状态\n");
printf("4. 管理员菜单\n");
printf("0. 退出系统\n");
printf("请选择操作: ");
int choice = getIntInput("", 0, 4); // 获取选择
switch (choice) {
case 1: // 客户取号
customerTakeNumber(&bank);
break;
case 2: { // 客户离开
printf("输入离开窗口号(0-%d): ", bank.windowCount-1);
int n = getIntInput("", 0, bank.windowCount-1);
deQueue(&bank, n); // 处理客户离开
assignCustomerToWindow(&bank); // 尝试分配新客户
break;
}
case 3: // 查询状态
queryQueueStatus(&bank);
break;
case 4: // 管理员菜单
adminMenu(&bank);
break;
case 0: // 退出系统
destroyBankSystem(&bank); // 释放内存
printf("感谢使用,再见!\n");
exit(0);
}
}
return 0;
}评估代码