【排序和搜索】药店老板的卖药策略

2023-11-24 14:50

本文主要是介绍【排序和搜索】药店老板的卖药策略,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

对比上一个银行实验来说,这次实验的难度,是更考验大家的编程能力的👩‍🔬
因为这次,所有的代码从零开始!所有的策略都要自己去想qwq)


不过,这篇文章我主要想聊一聊策略,因为我的程序没有任何高级算法【大概就是你希望的那种算法,可以依据不同的条件去迭代调节策略,但很不幸,短时间内我写不出来😢】,所以我完全是按照策略找一些合适的数据结构和基础到不行的排序、搜索算法来实现的,不过收益还不错。此刻让我们高呼数学yyds!!
这里强推一位大佬的退火算法!!戳戳!


Content

  • 1 题目重现
  • 2 最初的策略
    • 2.1 预处理丢弃药品
    • 2.2 每日处理过期药品
    • 2.3 药品销售策略
    • 2.4 药品上架策略
    • 2.5 问题
  • 3 重构后的策略
    • 3.1 销售策略中的排序
    • 3.2 上架策略中的选择
    • 3.3 降低算法复杂度
  • 3 实验结果
  • 4 特别鸣谢

1 题目重现

你是一家药店的老板,这个月你从供货商手里收到了一批共50个药品

√ 其中每个药品有独立的进价和保质期,其中进价的区间为[20,30]元,保质期的剩余天数为[1,15]天

√ 你每天可以陈列出10个药品在商店中售卖,每天会有三个顾客各过来购买一个药品。

√ 药品的定价只能为商品进价加上【-1.5,-1, -0.5, 0, 2 ,4 ,6】元,不得随意定价。

√ 每位顾客购买的逻辑是,买其中最便宜的药品,如果说最便宜的药品价格一致则购买保质期长的药品。

√ 三位顾客会依次购买药品。

√ 药品如果没有陈列在商店中,而是存放在仓库时,会收取管理费,其中保质期低于5天的每天收取1元管理费,其余的每天收取0.5元。

√ 你的目标是,10天之后,你的利润(售出商品售价总和-售出商品进价总和-支付仓库的管理费用-10天内过期/丢弃商品的进价)最大

不知道各位看到这道题目的感想是什么,我当时看到这个题目的时候,笑容突然凝固了呢 =)

老师非常温馨地提醒了我们:不要想暴力枚举所有的情况哟~

因为去年我们的某位学长就把所有的情况列了一遍。验收的时候,“老师,你看我开始跑了哈”【40分钟过去了】“老师我跑好啦!” 所以说,大力出奇迹这句话在哪里都能用上

2 最初的策略

我把策略分为以下几个部分,分别来讲一讲

  1. 预处理必须要丢弃的药品
  2. 每日处理过期药品
  3. 药品售卖策略
  4. 药品上架策略

2.1 预处理丢弃药品

仓库存放药品选取的数据结构为:哈希表

⭐哈希函数为 d = H a s h ( k e y ) = k e y d=Hash(key)=key d=Hash(key)=key,其中,key为保质期天数
⭐按照日期遍历哈希表,若在此日期前的所有药品数大于 3 × d a t e 3\times date 3×date,则删去其中价格最低的药品。

//To delet the medicine which have to be discarded absolutely
void Pre_Process(WH*w,ofstream &del)//delete the node which is impossible to save.
{int d=1;int counts=0;char ID[3];for(d=1;d<10;d++){counts+=w->map->data[d].count;   if(counts>3*(d)){for(int x=counts-3*d;x>0;x--){ElementType who=*GetWhoDelet(w->map,d);HashNodeDel(w,who.id,who.day);itoa(who.id,ID,10);del<<'0'<<'\t'<<ID<<'\n';w->gross-=who.price;w->num--;counts--;}}}w->ln=0;w->num=0;
}
//Get Which Medicine to be deleted
ElementType *GetWhoDelet(HashMap *mp,int day)
{ElementType * who=(ElementType*)malloc(sizeof(ElementType));who=PutOn(*mp->data[1].cur);for(int i=1;i<=day;i++){if(mp->data[i].stat==Empty){continue;}ElementType *temp=PutOn(*mp->data[i].cur);while(temp){if(Larger(*who,*temp)){who=PutOn(*temp);    }temp=temp->next;}}return who;
}

2.2 每日处理过期药品

防止某些步骤中有疏漏,每天要按照药品的保质期和日期进行保护处理,以达到题目要求

⭐在此之前,已经将哈希表中的药品按照保质期分为了两个数组,一个是保质期10天以下 L e s s T h a n T e n LessThanTen LessThanTen,另一个是保质期10天以上 M o r e T h a n T e n MoreThanTen MoreThanTen
⭐因为只模拟10天,只可能在第一个数组中出现过期药品

void Fix(WH *w,int date,int n,ofstream &del)
{for(int i=0;i<n;i++){if((w->LessThanTen[i].day-date)<0 && (w->LessThanTen[i].id!=-1)){char day[3];char ID[3];itoa(date,day,10);itoa(w->LessThanTen[i].id,ID,10);del<<day<<"\t"<<ID<<"\n";w->gross-=w->LessThanTen[i].price;//HashNodeDel(w,w->LessThanTen[i].id,w->LessThanTen[i].day);w->ln--;w->LessThanTen[i].id=-1;w->num--;}}
}

2.3 药品销售策略

如何确定药品的销售顺序,是策略中的重点之一。在最初的策略里,我一直在想同时顾及到保质期和进价,得出以下的一些想法
⭐ 针对保质期小于10天数组,以 P r i c e + M a n a g e F e e Price+ManageFee Price+ManageFee为关键码进行降序排序,将序号大于30的丢弃。
⭐针对保质期大于等于10天数组,以Price为关键码进行降序排序
⭐定性的来看,这种处理方法可以使得不好卖的又比较便宜的提前扔掉,少一点管理费;但实际上,这会导致保质期比较短的本来可以用策略卖出去,但提前被扔掉,亏损会增加
⭐排序算法:快速排序

void OrderLess(WH*w,int n)
{QSort3(w->LessThanTen,0,n);float min=w->LessThanTen[0].price;int index;if(n>=30){for(int i=30;i<=n;i++){for(int j=0;j<n;j++){if(w->LessThanTen[j].id!=-1 && w->LessThanTen[j].price<min){index=j;break;}}HashNodeDel(w,w->LessThanTen[index].id,w->LessThanTen[index].day);w->LessThanTen[index].id=-1;w->gross-=w->LessThanTen[index].price;}}
}void OrderMore(WH*w,int n)
{QSort33(w->MoreThanTen,0,n);
}

2.4 药品上架策略

这个策略非常重要!它关系到一个重要的成本问题,就是管理费!

药品上架:从LessThanTen中按顺序上架,尽可能使得按照顺序每天都卖出三个指定药品。前三个id非-1的药品作为当日要卖的药品,若货架没有补齐,按照一定规则先上架理论上不影响前三个药品上架的药品,直到货架堆满为止;当小于10天的药品卖完之后,大于10天的药品中选取保质期相对短的上架,以减少管理费的产生。

上架规则:前三个药品的最高原始进价为标准线,后续药品按加6元后比标准线高的,予以理论上架可能。之后,对于后7个药品进行以定价为关键码的从低到高排序。排序完成后,针对7-10每一个药品,计算在它存在时,前三个药品能够定价多少,若计算出此时的利润小于 18-(后续药品管理费),将该药品下架。(示意图如下)

tips:当货架上所有药品均为保质期大于10天时,不需要做此步骤,只需在10个药品中找到价格最低的3个药品进行售卖即可
上架示意图

2.5 问题

眼看就到了ddl前的最后一个晚上,谁能想到去年教辅的策略那么佛系(只要我的策略够烂,同学们就都能满分 ),今年教辅的策略还算挺不错,赚的钱还真多。我连夜修改代码,一整个重构了所有的排序和上架原则,人类的潜能真是无穷无尽【反正就很神奇,很多看似完不成的任务,找不出的bug,改不好的代码,最后都能做好,只要你一直做:)

我和教辅的受益差的不是一点半点,一整个就在亏钱…就是我前面提到的销售策略和上架策略,没有抓住主要矛盾,顾虑太多旁支末节,导致反而亏本

3 重构后的策略

重构的策略主要就在三个方面
⭐销售策略中的排序
⭐上架策略中的选择
⭐降低算法复杂度

3.1 销售策略中的排序

⭐排序关键码改为:保质期(小于10天数组),进价(大于等于10天数组)

//For the MoreThanTen Array
void QSort33(ElementType A[],int low, int high)
{int i,j;int k,l;ElementType temp,empty;for(k=0;k<=high;k++){if(A[k].id==-1){empty=A[k];for(l=k;l<high;l++){A[l]=A[l+1];}A[l]=empty;high--;k--;}}if(low>=high) return;i=low;j=high;temp=A[SelectPivot_fix(&A,low,high)];while(i<j){while(i<j && A[j].price<temp.price)j--;//Less(A[j],temp)if(i<j){A[i]=A[j];i++;}while(i<j  && A[i].price>=temp.price)i++;//Largerequal(A[i],temp)if(i<j){A[j]=A[i];j--;}}A[i]=temp;QSort33(A,low,j-1);QSort33(A,j+1,high);
}//for the LessThanTen Array
void QSort3(ElementType A[],int low, int high)
{int i,j;int k,l;ElementType temp,empty;for(k=0;k<=high;k++){if(A[k].id==-1){empty=A[k];for(l=k;l<high;l++){A[l]=A[l+1];}A[l]=empty;high--;k--;}}if(low>=high) return;i=low;j=high;temp=A[SelectPivot_fix(&A,low,high)];while(i<j){while(i<j && (A[j].day>temp.day || (A[j].day==temp.day && A[j].price>temp.price)))j--;//Less(A[j],temp)if(i<j){A[i]=A[j];i++;}while(i<j  && (A[j].day<temp.day || (A[j].day==temp.day && A[j].price<temp.price)))i++;//Largerequal(A[i],temp)if(i<j){A[j]=A[i];j--;}}A[i]=temp;QSort3(A,low,j-1);QSort3(A,j+1,high);
}

3.2 上架策略中的选择

⭐当 L e s s T h a n T e n LessThanTen LessThanTen数组中的药还没卖完时,在其中选取7个药品(管理费为1元的优先)进行上架(且价格上不能影响3个药品的售卖),用来节约管理费
⭐当 L e s s T h a n T e n LessThanTen LessThanTen数组卖完了但还没结束时, M o r e T h a n T e n MoreThanTen MoreThanTen数组按照保质期长短先后上架(为了节约管理费),后面7个药品(管理费1元的优先),以不影响药品售卖的原则按数组顺序上架(数组内已经按价格降序排过了)

这一段代码我写的不好,大家可以按照这个思路用自己的风格实现√

3.3 降低算法复杂度

那个编译器吧,像是有什么大饼,复杂度高一点的地方它就给你判断成死循环,就给你中断运行了。但其实你用单步调试完全🆗。当你遇到这种问题的时候,一定要去检查自己的算法,是不是有多余的循环、遍历、搜索过程,把这些进行简化

我自己因为用了哈希表作为一个存储结构,一开始在卖药过程中我会对哈希表进行节点的删除,为了表示药品已经被卖出或者丢弃。但这就导致要对哈希表进行非常多次的遍历、删除操作,算法复杂度过高。

在进行上面这些操作的时候,我忽略了自己已经把药品分类放在了两个数组里,这俩数组的规模并不大。

所以,我通过把卖掉或丢弃的药品id置为-1,减少了数组删除元素的复杂度 O ( n ) O(n) O(n),只要每次使用时记得加上 i d ! = − 1 id\ ! =-1 id !=1的判断条件即可。

经过这样类似的一番修改,终于还算是成功的做了个药店,它能赚钱啦!

3 实验结果

数据data教辅利润我的利润
data134.538
data236.540
data32938.5
data435.544
data53337

一个晚上的极限修改,让我从几乎每个数据集亏本,到每个数据集赚的都不少,这之中策略的决策真的十分重要。

4 特别鸣谢

特别感谢哆啦B梦同学的大力支持,虽然表面上说着

不知道不知道,自己想去

但是,还是花费了宝贵的一个小时跟我讨论这个题目的数学策略,让我能完全理清思路,也为我一个晚上重构代码奠定了深厚的基础.让我们再次高呼

数 学 ,y y d s !

题外话:不得不说我写代码真的很烂,别人几百行写完的我怎么就要写上千呢…不过这个实验好歹是做完了🎉

这篇关于【排序和搜索】药店老板的卖药策略的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/422119

相关文章

认识、理解、分类——acm之搜索

普通搜索方法有两种:1、广度优先搜索;2、深度优先搜索; 更多搜索方法: 3、双向广度优先搜索; 4、启发式搜索(包括A*算法等); 搜索通常会用到的知识点:状态压缩(位压缩,利用hash思想压缩)。

hdu1240、hdu1253(三维搜索题)

1、从后往前输入,(x,y,z); 2、从下往上输入,(y , z, x); 3、从左往右输入,(z,x,y); hdu1240代码如下: #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#inc

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)

usaco 1.3 Mixing Milk (结构体排序 qsort) and hdu 2020(sort)

到了这题学会了结构体排序 于是回去修改了 1.2 milking cows 的算法~ 结构体排序核心: 1.结构体定义 struct Milk{int price;int milks;}milk[5000]; 2.自定义的比较函数,若返回值为正,qsort 函数判定a>b ;为负,a<b;为0,a==b; int milkcmp(const void *va,c

hdu 1285(拓扑排序)

题意: 给各个队间的胜负关系,让排名次,名词相同按从小到大排。 解析: 拓扑排序是应用于有向无回路图(Direct Acyclic Graph,简称DAG)上的一种排序方式,对一个有向无回路图进行拓扑排序后,所有的顶点形成一个序列,对所有边(u,v),满足u 在v 的前面。该序列说明了顶点表示的事件或状态发生的整体顺序。比较经典的是在工程活动上,某些工程完成后,另一些工程才能继续,此时

hdu 4517 floyd+记忆化搜索

题意: 有n(100)个景点,m(1000)条路,时间限制为t(300),起点s,终点e。 访问每个景点需要时间cost_i,每个景点的访问价值为value_i。 点与点之间行走需要花费的时间为g[ i ] [ j ] 。注意点间可能有多条边。 走到一个点时可以选择访问或者不访问,并且当前点的访问价值应该严格大于前一个访问的点。 现在求,从起点出发,到达终点,在时间限制内,能得到的最大

AI基础 L9 Local Search II 局部搜索

Local Beam search 对于当前的所有k个状态,生成它们的所有可能后继状态。 检查生成的后继状态中是否有任何状态是解决方案。 如果所有后继状态都不是解决方案,则从所有后继状态中选择k个最佳状态。 当达到预设的迭代次数或满足某个终止条件时,算法停止。 — Choose k successors randomly, biased towards good ones — Close

hdu4277搜索

给你n个有长度的线段,问如果用上所有的线段来拼1个三角形,最多能拼出多少种不同的? import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;

《数据结构(C语言版)第二版》第八章-排序(8.3-交换排序、8.4-选择排序)

8.3 交换排序 8.3.1 冒泡排序 【算法特点】 (1) 稳定排序。 (2) 可用于链式存储结构。 (3) 移动记录次数较多,算法平均时间性能比直接插入排序差。当初始记录无序,n较大时, 此算法不宜采用。 #include <stdio.h>#include <stdlib.h>#define MAXSIZE 26typedef int KeyType;typedef char In