外卖派单骑手运单模拟程序

这是一次课程设计实验。
任务描述
你运行一家外卖快递服务店,负责一个区域内的外卖订单接收和餐食快递.你有一笔启动资金,可以招募外卖骑手帮你送餐,来赚取快递费。但你也会面临风险,本区域的订单你都有义务接收,不能拒绝,若拒单就会被工商部门吊销营业执照;但如果接收多个订单后,因为骑手来不及送达,导致某个订单超时未送达,客户会投诉,你会被罚款。
因此,你的任务就是制定聪明的调度策略,避免拒单被吊销营业执照(人工调度在订单高峰期可能会来不及接收),避免因为罚款而破产,并且尽可能赚更多的钱。


派送区域假设及约定
约定1: 你负责的外卖派送区域如图所示,该区域包含99的房间,每格是一个房间,既可以是下订单的食客家,也可以是接单的餐馆。
约定2: 方格之间的8
8条街道是骑手唯一可走的道路;骑手停在方格的上下左右街道,即算抵达。
约定3: 每个方格的宽高都一样,即骑手走过每个方格的距离一样,速度也一样,约定为骑手每走过一个方格花费1个时间单位,骑手从最左边直达最右边花费时间8个时间单位,即拐弯不花时间,经过路口不花时间。
约定4: 为了记录房间和骑手位置,坐标系定为17*17,约定左上角的房间逻辑坐标为(0,0),右下角房间的逻辑坐标为(16,16)。


运营规则及约束(1—10条)
约束-1:系统开始运营时,你有1000 2 300 作为运营资本; 约束-2:你必须有骑手才能接单,招聘一位骑手需投资300 ; 只要你有钱,骑手数量不限;在系统运营的整个期间,你都可以随时招聘骑手,但必须有足够的钱,不能拖欠;
约束-3:你负责的外卖派送区域内,发起的任何订单都必须接收;如果订单发起后,3个时间单位内没有派单给现有骑手,则视为拒单,你将被吊销营业执照,运营终止。
约束-4:派单的方式只能是将其按顺序派给指定骑手。派单的操作可以是人工派单,也可以是程序按调度策略自动派单。人工派单的操作比较复杂,需要用鼠标逐个选中现有未处理订单,将其分配给某个骑手。
约束-5:所有骑手的初始位置必须是同一位置;但起始位置需要在你的程序中自行设定。骑手初始位置的设定可以在程序中写死,也可以在系统启动时修改设置。注意:如果骑手初始在左上角,那么立刻接到一个(右下角>左上角)的订单时,可能超时。
约束-6:每个订单从下单时间开始,要求在30个时间单位内完成服务(先后抵达餐馆和食客家),否则算超时。满30个时间点订单若没有完成结单,客户会投诉导致立刻罚款50 ; 60 7 10 ; 满60个时间点订单若没有完成结单,属于恶意废单立刻破产。 约束-7:每完成一单且不超时,可收入10 ;
约束-8:无须考虑骑手负载限制,一位骑手可以带无限外卖;但超时未达要按约束-7条处罚;
约束-9:负债即破产!一旦破产,即刻停止运营,系统盘点每位骑手的接单数、完成数、超时数。
约束-10:系统运营期间,至少每个时间单位更新一次,显示当前钱数、每位骑手的位置、接单数、完成数、超时数、当前利润。


派单策略(基础部分)
策略x-1: 首先按下单时间顺序,将新订单放入待处理队列。
策略x-2: 根据骑手数量,将区域划分为几个子区域,分给每个骑手,注意预先保留一个跨子区域订单骑手。
策略x-3: 派单时,从待处理队列取出队首订单,判断属于哪个子区域,就分给哪个骑手,分派后此单出待处理队列,加入对应骑手的待送达队列;餐馆和食客不在一个区域的订单,分给跨子区域骑手。
策略x-4: 当跨区域骑手的待派送队列中元素超过预警值时(例如10个),再分出一个骑手作跨区域骑手,剩下骑手重新划分区域,更改仅对后续订单生效,已分配订单不受影响。注意:预警值是你自己在程序中设定的,根据经验设置。
策略x-5: 所有单派完后,开始轮流对所有骑手的待派送队列进行优化。优化策略是:取出队首订单,作为当前目标点;计算骑手当前位置和当前目标点的区域范围,然后扫描队列后续订单中的所有可达目标点(见后续解释),筛选出属于此区域内的,设计出合理的行走路线,只要按此路线当前目标点不超时,即可插入到队首目标点之前。


设计思路
贪心算法


代码实现

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
#include<Windows.h>
#include<vector>
#include "iostream"
#include "conio.h"
#include<algorithm>
#include"string"
using namespace std;
#define MAX 1000
#define MIN 20
int map[17][17] = { 0 };			//地图标记初始化,1,2,3对应骑手 ,4餐厅 5食客 
int money = 1000;			//项目资金变量 
int indent = 0;		//订单数量标记 (总数、完成数、超实数)
int fakuan = 0;
vector<int> peisongwanc;    //配送完成
vector<int >peisongweiwanc;//配送没有完成
int tme = 0, cnt = 0;	//程序运行时间 

int Area_value[17][17];
string fandan;//罚单记录
string wanc;//完成记录
class Rider;//声明
class INdent;//声明
vector<Rider> Vrider;//骑手
vector<INdent> Vindent;//订单
class coordinate
{
public:
	int x, y;
	int zhi;//根据位置计算距离 来排序
	int peisongshij;//记录当前的配送时间
	int dingdanbianhao;//记录着位置的订单编号
	int paduancandingorkehu;//判断是餐厅 还是客户 1是餐厅 2是客户

	coordinate() {
	}
	coordinate(int x1, int y1, int dingdanbianhao1, int paduancandingorkehu1, int peisongshij1) {
		x = x1;
		y = y1;
		dingdanbianhao = dingdanbianhao1;
		paduancandingorkehu = paduancandingorkehu1;
		peisongshij = peisongshij1;
	}
	coordinate(int x1, int y1) {
		x = x1;
		y = y1;
	}

	//判断a2 是否在a1 周围一个
	bool getpd(coordinate a1, coordinate a2) {
		int d1 = abs(a2.x - a1.x);
		int d2 = abs(a2.y - a1.y);

		if (d1 == 0 && d2 == 1)
			return true;
		if (d1 == 1 && d2 == 0)
			return true;
		return false;
	}
};
class INdent {
public:
	int n;			//序号
	int cnt;		//标记骑手 
	int flag;			//属性标记 ,1接受未配送 2配送未完成 3完成 
	int q_time = 0;		//下单时间,此变量用于文件读取
	int restaurant[2];		//餐馆坐标
	int client[2];			//顾客坐标 
	int shenyupeisongshijian;//剩余配送时间
};


//自定义最近的距离排序函数  
bool sortFun1(const coordinate& p1, const coordinate& p2)
{
	return p1.zhi < p2.zhi;//升序排列  
}
bool sortFun2(const INdent& p1, const INdent& p2)
{
	return p1.q_time < p2.q_time;//升序排列  
}


class Rider
{
public:
	int x, y;//棋手坐标
	int state;//0停泊 1配送中 2没货移动返回初始地址中
	vector<coordinate> vec1;    //默认初始化, 需要配送的位置

	int zhi;//根据位置计算距离 来排序
	Rider() {//骑手初始的位置
		x = 9;
		y = 8;
	}

	//判断是否可以将这个路线添加进来   cant餐厅位置 kehu客户位置  zhuangt=1 超时30不配送   zhuangt=1超时60不配送
	bool PDshifoujiatu(INdent& danzi, int standard) {

		coordinate cant(coordinate(danzi.restaurant[0], danzi.restaurant[1], danzi.n, 1, tme - danzi.q_time));
		coordinate kehu(coordinate(danzi.client[0], danzi.client[1], danzi.n, 2, tme - danzi.q_time));
		int tujinshi = vec1.size() + 2;//当前总的途径数
		int* dingdanpan = new int[vec1.size() + 2];
		int i, dqx, dqy;
		vector<coordinate> cunf;    //存放所有位置的订单
		vector<coordinate> zunfzuiyoutuj;//存放最优配送途径
		vector <coordinate>::iterator it;
		for (i = 0; i < tujinshi; i++) dingdanpan[i] = -1;

		for (it = vec1.begin(); it != vec1.end(); it++)//进行运行事件模拟
			cunf.push_back(*it);
		cunf.push_back(cant), cunf.push_back(kehu);
		dqx = this->x; dqy = this->y;//骑手当前坐标
		while (cunf.size() > 0) {
			//计算所有距离距离当前位置距离
			for (it = cunf.begin(); it != cunf.end(); it++)
				it->zhi = abs(it->x - dqx) + abs(it->y - dqy);
			//进行排序,包括餐厅和食客位置
			sort(cunf.begin(), cunf.end(), sortFun1);
			for (it = cunf.begin(); it != cunf.end(); it++) {
				//判断是不是餐厅
				if (it->paduancandingorkehu == 1) {
					zunfzuiyoutuj.push_back(*it);
					dqx = it->x, dqy = it->y;//假设骑手已经到这了
					for (i = 0; i < tujinshi; i++) {
						if (dingdanpan[i] == -1) {
							dingdanpan[i] = it->dingdanbianhao;//记录这个订单号
							break;
						}
					}
					cunf.erase(it);//删除这个位置的元素
					break;
				}
				else {
					for (i = 0; i < tujinshi; i++)if (dingdanpan[i] == it->dingdanbianhao)break;
					if (i < tujinshi) {
						zunfzuiyoutuj.push_back(*it);
						dqx = it->x, dqy = it->y;//假设骑手已经到这了
						cunf.erase(it);//删除这个位置的元素
						break;
					}
				}
			}
		}
		//判断这个最优值是否不会出现超时配送
		dqx = this->x; dqy = this->y;
		int leijijs = 0;//累计计时
		int bianliang;
		int value = 0;//存储可以收获的金额
		for (it = zunfzuiyoutuj.begin(); it != zunfzuiyoutuj.end(); it++) {
			bianliang = leijijs + abs(dqx - it->x) + abs(dqy - it->y);
			bianliang = bianliang % 2 == 0 ? bianliang / 2 : bianliang / 2 + 1;
			leijijs = leijijs + bianliang;
			if ((leijijs + it->peisongshij) >= 30 && standard == 1)return false;
			if ((leijijs + it->peisongshij) >= 60 && standard == 2)return false;//存在停运风险
			dqx = it->x, dqy = it->y;
		}
		vec1.erase(vec1.begin(), vec1.end());
		for (it = zunfzuiyoutuj.begin(); it != zunfzuiyoutuj.end(); it++)vec1.push_back(*it);
		delete[]dingdanpan;
		return true;
	}
	//添加一个单子
	void ADDdanzi(INdent& danzi) {
		if (state == 2)//如果该骑手正在返回初始位置的过程中
			vec1.erase(vec1.begin(), vec1.end());
		state = 1;

		//传入餐馆坐标
		vec1.push_back(coordinate(danzi.restaurant[0], danzi.restaurant[1], danzi.n, 1, tme - danzi.q_time));
		//传入客户坐标
		vec1.push_back(coordinate(danzi.client[0], danzi.client[1], danzi.n, 2, tme - danzi.q_time));
		danzi.flag = 1;
	}
	//骑手派单移动 
	void QIsyidong() {
		if (vec1.size() <= 0 && this->state == 0) {//当派单数为零时,退出运动函数
			return;
		}
		char a[100];
		string zhi;
		vector <coordinate> ::iterator it;
		//获取商家或者客户的位置
		it = vec1.begin();
		//如果在周围了 表示这单已经配送完成
		if (it->getpd(*it, coordinate(x, y)) == 1) {
			if (this->state == 2) {
				vec1.erase(vec1.begin(), vec1.end());
				this->state = 0;
				return;
			}
			if (it->paduancandingorkehu == 2) {
				sprintf(a, "%d", it->dingdanbianhao);
				zhi = a;
				//如果超时了
				if (it->peisongshij >= 30) {
					if (it->peisongshij >= 30) {
						money -= 50;		//罚款 
						fakuan = fakuan + 50;
						fandan = fandan + zhi;
					}
					if (it->peisongshij >= 60) {
						money = -99999999;//游戏结束
						fandan = fandan + zhi;
					}
				}
				else {
					money += 10;
					wanc = wanc + zhi;
				}
				peisongwanc.push_back(it->dingdanbianhao);//放入成功送单的容器
			}
			vec1.erase(it);//所到之地为餐厅,就消除该餐厅标记
		}
		if (vec1.size() <= 0 && state == 1) {//派单数为零时,退出运动函数
			state = 0;
			//返回初始位置
			if (this->x != 7 && this->y != 9) {
				vec1.push_back(coordinate(8, 8));
				this->state = 2;
			}
		}
		it = vec1.begin();
		//获取商家或者客户的位置

		coordinate weizhi = *it;

		if (
			(
			(y < weizhi.y && x % 2 != 0)
				&& (y + 1 == weizhi.y ? weizhi.y % 2 != 0 || abs(x - weizhi.x) == 1 : 1)
				|| (y == weizhi.y && weizhi.y % 2 == 0)
				) && y + 1 < 17
			) {
			y = y + 1;

		}
		else if (
			(
			(y > weizhi.y&& x % 2 != 0)
				&& (y - 1 == weizhi.y ? weizhi.y % 2 != 0 || abs(x - weizhi.x) == 1 : 1)
				|| (y == weizhi.y && weizhi.y % 2 == 0)

				) && y - 1 >= 0
			) {
			y = y - 1;
		}
		else if (
			(
			(x < weizhi.x && y % 2 != 0)
				&& (x + 1 == weizhi.x ? weizhi.x % 2 != 0 || abs(y - weizhi.y) == 1 : 1)
				|| (x == weizhi.x && weizhi.x % 2 == 0)
				) && x + 1 < 17
			)
		{
			x = x + 1;
		}
		else if (
			(
			(x > weizhi.x&& y % 2 != 0)
				&& (x - 1 == weizhi.x ? weizhi.x % 2 != 0 || abs(y - weizhi.y) == 1 : 1)
				|| (x == weizhi.x && weizhi.x % 2 == 0)
				) && x - 1 >= 0
			)
		{
			x = x - 1;
		}
		for (it = vec1.begin(); it != vec1.end(); it++) {//需要送达的地方的配送时间计算

			it->peisongshij = it->peisongshij + 1;
		}
		return;
	}
};

void indent_init()			//订单输入 
{
	INdent a;
	printf("请输入订单信息(序号  餐馆坐标x 餐馆坐标y 食客坐标x 食客坐标y ):");
	scanf("%d %d %d %d %d", &a.n, &a.restaurant[0],
		&a.restaurant[1], &a.client[0], &a.client[1]);
	a.flag = 4;
	indent++;
	Vindent.push_back(a);
}

void indent_init_txt()		//从文件读取
{
	FILE* fp;
	INdent a;
	fp = fopen("sales.txt", "r");
	if (!fp) {
		printf("文件打开失败");
		return;
	}
	while (!feof(fp))		//检测未到结尾 
	{
		fscanf(fp, "%d %d %d %d %d %d", &a.n, &a.q_time, &a.restaurant[0],
			&a.restaurant[1], &a.client[0], &a.client[1]);
		a.flag = 4;
		if (feof(fp))break;
		Vindent.push_back(a);
		indent++;
	}
	sort(Vindent.begin(), Vindent.end(), sortFun2);
	puts("读入完成");
	fclose(fp);
}

void out_txt()		//文件输出
{
	if (wanc == "" && fandan == "")return;
	string a;
	FILE* fp;
	vector <coordinate> ::iterator it1;
	int i = 1;
	fp = fopen("output.txt", "at+");
	fprintf(fp, "时间:%d\n", tme);
	fprintf(fp, "钱:%d\n", money);
	fprintf(fp, "接单数:%d\n", indent);
	fprintf(fp, "完成数:%d;结单:%s;\n", peisongwanc.size(), wanc.c_str());
	fprintf(fp, "超时数:%d;罚单:%s;\n", peisongweiwanc.size(), fandan.c_str());
	for (vector <Rider> ::iterator it = Vrider.begin(); it != Vrider.end(); it++) {
		fprintf(fp, "骑手%d位置:%d,%d; 停靠:", i, it->x, it->y);
		if (it->vec1.size() > 0) {
			it1 = it->vec1.begin();
			if (it1->paduancandingorkehu == 1) {
				fprintf(fp, "餐馆 %d %d;", it1->x, it1->y);
			}
			if (it1->paduancandingorkehu == 2) {
				fprintf(fp, "食客 %d %d;", it1->x, it1->y);
			}
		}
		fprintf(fp, "\n");
		i = i + 1;
	}
	fclose(fp);
}

int Time()		//计时函数,返回1满一秒 
{
	Sleep(100);		//延时1s
	cnt = cnt + 1;
	if (cnt % 2 == 0)
		return 1;
	return 0;
}

//自定义最近的骑手排序函数  
bool sortFun(const Rider& p1, const Rider& p2)
{
	return p1.zhi < p2.zhi;//升序排列  
}
int send_t()		//派发订单
{
	vector <Rider> ::iterator it;
	vector <INdent> ::iterator nit;
	int Is_sucess = 0;//派单是否成功
	for (nit = Vindent.begin(); nit != Vindent.end(); nit++) {
		if (tme < nit->q_time)continue;//当前时间小于下单时间
		if (nit->flag == 1)continue;
		Is_sucess = 0;
		//计算每个骑手距离餐馆的位置
		for (it = Vrider.begin(); it != Vrider.end(); it++) {
			it->zhi = abs(it->x - nit->restaurant[0]) + abs(it->y - nit->restaurant[1]);
		}
		//排序寻找最近的骑手
		sort(Vrider.begin(), Vrider.end(), sortFun);
		//开始派单
		int standard = 0;
		for (it = Vrider.begin(); it != Vrider.end(); it++) {
			//表示这个骑手已经在配送了
			if (money >= 300) standard = 1;
			else standard = 2;
			if (it->state == 1) {
				//检查是否可以顺路
				if (it->PDshifoujiatu(*nit, standard)) {
					Is_sucess = 1;//派单成功
					nit->flag = 1;
					break;
				}

			}
			else {
				it->ADDdanzi(*nit);
				Is_sucess = 1;//派单成功
				nit->flag = 1;
				break;
			}
		}
		if (Is_sucess == 0 && money > 300) {//雇佣新的骑手
			money = money - 300;
			Rider rid;
			rid.state = 1, rid.ADDdanzi(*nit);
			Vrider.push_back(rid);
			nit->flag = 1;
		}
	}
	return 0;
}
//骑手派单运动
void peo_sport()
{
	vector <Rider> ::iterator it;
	for (it = Vrider.begin(); it != Vrider.end(); it++) {
		//开始配送
		if (it->state == 1 || it->state == 2)it->QIsyidong();
	}
}

int check_money()		//订单状态检测 ,返回0结束 
{
	int i;
	if (money < 0)		//破产返回0 
	{
		return 0;
	}
	return 1;
}

void _gotoxy(int x, int y) {

	COORD c;
	c.X = x;
	c.Y = y;
	HANDLE had = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleTextAttribute(had, 10 | 3);
	SetConsoleCursorPosition(had, c); //修改当前光标的位置
}
void show()		//显示函数
{
	int i, j;
	for (i = 0; i < 17; i++) {
		for (j = 0; j < 17; j++) {
			if (i % 2 == 0 && j % 2 == 0)
				map[i][j] = 1;
			else
				map[i][j] = 0;
		}
	}
	vector <Rider> ::iterator it;
	vector <coordinate> ::iterator coord;
	i = 1;

	for (it = Vrider.begin(); it != Vrider.end(); it++) {//棋手遍历

		map[it->y][it->x] = 3 + i;//标记骑手位置
		i = i + 1;//大于等于4的都是骑手
		if (it->state == 1) {//标记正在运输中的骑手需要到达的地方
			for (coord = it->vec1.begin(); coord != it->vec1.end(); coord++) {//标记餐厅和客户位置

				if (coord->paduancandingorkehu == 1) {
					map[coord->y][coord->x] = 2;//餐厅位置
				}
				if (coord->paduancandingorkehu == 2) {
					map[coord->y][coord->x] = 3;//客户位置
				}
			}
		}
	}
	for (i = 0; i < 17; i++)//输出模拟图
	{
		for (j = 0; j < 17; j++)
		{
			_gotoxy(6 * i, j);//定光标 
			if (map[i][j] == 1)printf("[    ]");
			if (map[i][j] == 0)printf("     ");
			if (map[i][j] == 2)printf("[餐厅]");
			if (map[i][j] == 3)printf("[食客]");
			if (map[i][j] > 3)printf("骑手%d", map[i][j] - 3);
		}
		printf("\n");
	}

	printf("当前总钱数:%d   当前余额:%d                         \n", money + Vrider.size() * 300, money);
	i = 1;
	for (vector <Rider> ::iterator it = Vrider.begin(); it != Vrider.end(); it++) {
		printf("骑手%d的位置(x,y):(%d,%d)                        \n", i, it->x, it->y);
		i = i + 1;
	}
	printf("接单数:%d                                            \n", indent);
	printf("完成数:%d                                            \n", peisongwanc.size());
	printf("超时数:%d                                            \n", peisongweiwanc.size());
	printf("时间:%d                                              \n", tme);
	printf("输入选择的功能\n(1.订单输入  2.刷新屏幕):");
}
int main()
{
	int i;
	char cnt;
	printf("系统即将启动······\n");
	system("cls");
	while (1)
	{
		show();
		if (_kbhit()) {
			cnt = _getch();
			system("cls");
			if (cnt == '1')
			{
				printf("请选择(1.文件输入  2.命令行输入)");
				cnt = getchar();
				if (cnt == '1')
					indent_init_txt();
				else if (cnt == '2')
					indent_init();
			}
			system("cls");
			system("cls");
		}
		else {
			if (Time() == 1)
			{

				out_txt();
				fandan = "";
				wanc = "";
				tme = tme + 1;
			}
			send_t();
			peo_sport();
		}
		if (!check_money())
		{
			printf("破产!!!");
			printf("账户余额:%d\t接单数:%d\t完成数:%d\t超时数:%d\n", money, indent, peisongwanc.size(), peisongweiwanc.size());

			i = 1;
			for (vector <Rider> ::iterator it = Vrider.begin(); it != Vrider.end(); it++) {
				printf("骑手%d的位置(x,y):(%d,%d) 停靠:", i, it->x, it->y);

				i = i + 1;
			}
			break;
		}
	}
	system("cls");
}

发布了222 篇原创文章 · 获赞 48 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_44116998/article/details/104810207
今日推荐