课题内容和要求
顾客排队买蛋挞问题。有N个顾客排队,每人最多买M个。烘焙员每次烘焙1到K个蛋挞放入盘中,顾客只能购买盘中的蛋挞,未达到M个需重新排队。输出每个顾客购买情况和完成顺序。
例如——
输入:N=9,K=5;M=7
输出的购买情况为:
首次购买数量 | 第二次购买数量 | 第三次购买数量 | |
---|---|---|---|
顾客1 | 1 | 5 | 1 |
顾客2 | 2 | 1 | 4 |
顾客3 | 3 | 2 | 2 |
顾客4 | 4 | 3 | |
顾客5 | 5 | 2 | |
顾客6 | 1 | 6 | |
顾客7 | 2 | 2 | 3 |
顾客8 | 3 | 2 | 2 |
顾客9 | 4 | 3 |
购买到M个蛋挞的顾客的顺序为:4,5,6,9,1,2,3,7,8
数据结构说明
结构体Shop,存放商店营业信息,包括顾客数量N,蛋挞每次生产的上限K,每个顾客最多购买的数量M,以及每位顾客购买信息用结构体数组cu存放。数组result用于存放购买到M个蛋挞的顾客顺序,例如result[0]=i,表示顾客i第一个购买完M个蛋挞。list_col用于记录购买情况表的列数,控制最后输出购买情况的表头。
结构体Customer,存放顾客还需要购买蛋挞的数量(初始时需要购买数量M),顾客购买次数record,以及每次购买的记录以链表的形式按次序存储。
结构体Cal,存放顾客购买记录的链表结点,n表示该次购买的数量,next指向下一次购买记录结点。
算法设计
算法步骤:
(1)根据输入初始化shop当中的所有信息。
(2)初始化控制数组cu元素循环的下标cu_cal为0,初始化控制数组result下标的re_cal为0,标记flag初始化为0,蛋挞每次生产数量i初始化为0。
(3)若flag不为1,执行以下循环,直至flag=1。
a.i+1,若i>K重新置i=1,并将i加入到盘K_当中
b.通过循环判断顾客还需购买的数量mount是否为0,若为0表示购买完毕,数组cu下标cu_cal+1后对顾客数量N取模,遍历完数组cu直至找到一个还没购买完M个蛋挞的顾客,退出b步骤循环。该循环不会死循环,总能找到最后一个顾客未购买完M个蛋挞,并在后续步骤中将flag置为1,在下一轮大循环((3)步骤)中退出,不会再进到该循环。
c.判断盘中K_的数量是否大于顾客还需够买的数量mount,若mount大于K_则将mount的值减去K_的值,并添加该顾客的购买记录,置K_为0,接着判断该顾客的购买记录数record是否大于购买情况结果列数list_col,若大于则将record值赋值给list_col;若mount小于或等于K_,则将K_的值减去mount的值,并添加该顾客的购买记录,接着判断该顾客的购买记录数record是否大于购买情况结果列数list_col,若大于则将record值赋值给list_col,置该顾客的mount为0,此时该顾客已经购买完M个蛋挞,将该顾客的序号cu_cal加1后(加1后才是顾客真正的序号)赋值给购买顺序结果result[re_cal],re_cal加1使数组result下标后移。判断re_cal的值是否等于顾客数量N,若等于,表示所有顾客都已购买完M个蛋挞,置flag为1。
d.cu_cal加1并对N取模,使数组cu下标后移
(4)依据list_col值循环输出购买情况表头“第i+1次购买数量”
(5)依据顾客N值循环输出顾客的序号,并且遍历购买记录链表输出购买数量
(6)最后按顺序循环输出存放在数组result中顾客购买顺序
详细设计
程序包含文件shop_main.c、shop.h、shop.c
shop_main.c
#include <stdio.h>
#include <stdlib.h>
#include "shop.h"
int main()
{
Shop *sh = (Shop*)malloc(sizeof(Shop));
int N = 0; // 顾客数量
int K = 0; // 商店生产蛋挞的上限
int M = 0; // 顾客需要购买的数量
printf("蛋挞店\n");
printf("请输入顾客数量:\n");
scanf("%d",&N);
printf("请输入商店生产蛋挞的上限:\n");
scanf("%d",&K);
printf("请输入顾客需要购买的数量:\n");
scanf("%d",&M);
Init_shop(sh,N,K,M); // 调用函数初始化商店营业信息
Shop_work(sh); // 调用函数生成购买情况并输出
printf("回车结束程序...\n");
getchar(); // 将输入顾客需要购买的数量时按下的回车接收
getchar(); // 等待输入回车结束程序运行
free(sh);
}
shop.h
#ifndef __SHOP_H__
#define __SHOP_H__
typedef struct cal // 存放顾客一次购买记录的结构体
{
int n; // 存放购买数量
struct cal *next; // 指向下一个结点的指针
}Cal;
typedef struct customer // 存放顾客购买信息的指针
{
int mount; // 存放还需要购买的数量
int record; // 存放购买次数
Cal *first; // 存放购买记录的链表头指针
}Customer;
typedef struct shop // 商店营业信息
{
Customer *cu; // 存放顾客信息的数组头指针
int *result; // 将已经购买到M个蛋挞顾客按顺序存入数组
int list_col; // 记录生成结果表的列数,用于构造结果表
int N; // 顾客数量
int K; // 生产蛋挞的上限
int K_; // 盘中被购买后剩余的蛋挞数量
int M; // 顾客需要购买蛋挞的数量
}Shop;
void Init_shop(Shop *shop,int N, int K, int M); // 商店初始化
void Creat_cal(Customer *cu, int n); // 购买记录结点创建
void Shop_work(Shop *shop); // 商店程序运行
#endif
shop.c
#include "shop.h"
#include <stdio.h>
#include <stdlib.h>
/**
* @brief 商店初始化
* @param shop 指向商店营业信息的指针
* @param N 顾客数量
* @param K 商店生产蛋挞的上限
* @param M 顾客需要购买蛋挞的数量
* @return void
*/
void Init_shop(Shop *shop,int N, int K, int M)
{
shop->list_col = 0; // 初始化购买情况结果列数为0
shop->N = N;
shop->cu = (Customer*)malloc(N*sizeof(Customer)); // 动态申请存顾客信息的数组空间
shop->result = (int*)malloc(N*sizeof(int)); // 动态申请存放顾客编号
if(!shop->cu&&!shop->result)
{
printf("申请空间失败————顾客数组,购买顺序\n");
}
for(int i = 0;i<N;i++) // 初始化每个顾客信息
{
shop->cu[i].mount = M;
shop->cu[i].first = NULL;
shop->cu[i].record = 0;
shop->result[i] = 0;
}
shop->K = K;
shop->K_ = 0;
shop->M = M;
}
/**
* @brief 购买记录结点创建
* @param cu 指向顾客信息的指针
* @param n 该次购买蛋挞的数量
* @return void
*/
void Creat_cal(Customer *cu, int n)
{
if(cu->first==NULL) // 当没有购买记录时创建第一次购买记录
{
cu->first = (Cal*)malloc(sizeof(Cal));
if(!cu->first)
{
printf("申请空间失败————购买记录");
}
cu->first->n = n;
cu->record+=1;
cu->first->next = NULL;
return;
}
Cal *p = cu->first;
for(;p->next; p=p->next); // 将p指向最后一个不为空的结点,链表元素尾插
p->next = (Cal*)malloc(sizeof(Cal));
if(!p->next)
{
printf("申请空间失败————购买记录");
}
p->next->n = n;
cu->record += 1;
p->next->next = NULL;
}
/**
* @brief 商店程序运行
* @param shop 指向商店营业信息的指针
* @return void
*/
void Shop_work(Shop *shop)
{
int cu_cal = 0; // 数组下标,用于循环顾客排队购买蛋挞
int re_cal = 0; // 数组下标,用于result数组的计数,每当一个顾客购买完所需的蛋挞,+1
int flag = 0; // 标记,用于提前退出循环
int i = 0; // 蛋挞生产计数1~K
while(!flag)
{
i++;
if(i>shop->K) // 当蛋挞生产计数大于K时,重新置为1开始累加
{
i=1;
}
shop->K_+=i; // 将该次生产的蛋挞数加入到被购买后剩余的蛋挞数量中
while(shop->cu[cu_cal].mount==0) // 判断顾客是否购买完蛋挞,如果购买完,将数组下标向后移
{
// 该循环不会死循环,总能找到最后一个顾客未购买完M个蛋塔,
// 并在后续步骤中将flag置为1,在下一轮大循环中退出,不会再进到该循环
cu_cal = (cu_cal+1)%shop->N; // 数组下标后移
}
if(shop->cu[cu_cal].mount>shop->K_) // 顾客还需要购买的数量大于盘中剩余数量
{
shop->cu[cu_cal].mount-=shop->K_;
Creat_cal(&(shop->cu[cu_cal]),shop->K_); // 调用函数创建购买记录结点
shop->K_ = 0;
if(shop->list_col<(shop->cu[cu_cal].record)) // 如果顾客的购买记录数大于结果表的列数,更新列数
{
shop->list_col = shop->cu[cu_cal].record;
}
}
else // 顾客还需要购买的数量小于或等于盘中剩余数量
{
shop->K_ -= shop->cu[cu_cal].mount;
Creat_cal(&(shop->cu[cu_cal]),shop->cu[cu_cal].mount); // 调用函数创建购买记录结点
if(shop->list_col<(shop->cu[cu_cal].record))
{
shop->list_col = shop->cu[cu_cal].record;
}
shop->cu[cu_cal].mount = 0;
shop->result[re_cal] = cu_cal+1; // 顾客所需数量已足够,将顾客编号记录在数组中
re_cal +=1; // 数组下标后移
if(re_cal==shop->N) // 如果re_cal等于顾客数量,则表示所有顾客都购买完蛋挞,置flag标志位为1
{
flag = 1;
}
}
cu_cal = (cu_cal+1)%shop->N; // 数组下标后移
}
// 将购买情况输出
printf("购买情况如下:\n");
printf(" ");
for(i = 0;i<shop->list_col;i++) // 输出表头
{
printf("第%d次购买数量 ",i+1);
}
printf("\n");
for(i = 0;i<shop->N;i++)
{
Cal *p = shop->cu[i].first;
printf("顾客%2d",i+1);
for(;p;p = p->next) // 将每个顾客购买信息一行一行的输出
{
printf("%13d",p->n);
}
printf("\n");
}
printf("购买到%d个蛋挞的顾客的顺序为:",shop->M);
for(i = 0;i<shop->N;i++)
{
printf("顾客%d ",shop->result[i]);
}
printf("\n");
}
测试数据及其结果分析
运行结果1(题目例子):
运行结果2: