1026 Table Tennis

在这里插入图片描述
在这里插入图片描述

题目大意

k张桌子,玩家到达后总是选择编号最小的桌子。如果训练时间超过2h会被压缩成2h,如果到达时候没有球桌空闲就变成队列等待。 k张桌子中m张是vip桌,如果vip桌子有空闲,而且队列里面有vip成员,那么等待队列中的第一个vip球员会到最小的vip球桌训练。如果vip桌子空闲但是没有vip来,那么就分配给普通的人。如果没有vip球桌空闲,那么vip球员就当作普通人处理,也就是先来先服务。
现在给出每个球员的到达时间、要玩多久、是不是vip(是为1不是为0)。给出球桌数和所有vip球桌的编号,求出在关门前所有得到桌子的球员的到达时间、训练开始时间、等待时长(取整数,四舍五入),营业时间为8点到21点。如果在21:00之前没有排的上队,那么就不需要服务。

思路解析

算是PAT中比较麻烦的题目了。
本题的排队模型是“非紧凑的”,对于非紧凑的排队问题,同样只关心队头元素,特殊情况,比如本题中的VIP情况,顶多让他插个队,提前处理。目前也只有这种方法可以达到最优解,笔者尝试了几种不同方案(末尾会给出,大家可以对比一下)均会超时,顶多拿到19分。

具体思路如下:将玩家按到达的时间升序排序。用一个指针i选中当前队列中的第一个人,另一个指针vipid指向队列中等待的第一个VIP(为了给VIP开后门用),选出最先服务完的桌子。
如果是会员桌子,那就一定要尽量保证“物尽其用”,尽量服务VIP,先检查vipid,如果他在当前等待队列中,(也就是说在桌子服务完之前就已经在等待了),那么桌子分配给他;没有会员就分配给普通玩家。
如果是普通桌子,就要先来先服务,但是如果最先来的这个人(也就是i指针指向的这个人)是个VIP,就一定要保障他的会员权益,先检查会员桌子是不是就绪(也就是在他来之前就已经空闲了),如果是的话,就分配给他会员桌子;没有会员桌子的话,再把他当做普通人分配普通桌子。流程图如下:
Created with Raphaël 2.2.0 开始 选出最早空闲的桌子 是会员桌子? 有会员在等待? 分配给会员 结束 分配给队头的人 队头是会员? 有空闲的会员桌子? yes no yes no yes no yes no

示例代码

#include<iostream>
#include<vector>
#include<algorithm>
#include<math.h>
using namespace std;
struct person{
public:
	int come, get,play,wait;
	bool isvip = false;
}pperson;
struct table{
public:
	bool isvip = false;
	int present = 8 * 3600,num = 0;
};
vector<person> vec,res;//res存放被服务的人
vector<table> tables;//角标就是编号
int getVIP(int index) {
	index++;
	while (index < vec.size() && vec[index].isvip == false) index++;
	return index;
}
void alloac(int p, int t) {
	vec[p].get = tables[t].present < vec[p].come ? vec[p].come : tables[t].present;		
	if (vec[p].get < 21 * 3600) {
		res.push_back(vec[p]);
		tables[t].num++;
		tables[t].present = vec[p].get + vec[p].play;
	}
}
bool cmp(person p1,person p2) {
	return p1.come < p2.come;
}
bool cmp2(person p1, person p2) {
	return p1.get < p2.get;
}
int main() {
	int n,k,p;
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		int hour, min, sec, play, flag;
		scanf("%d:%d:%d %d %d", &hour, &min, &sec, &play, &flag);
		pperson.come = hour * 3600 + min * 60 + sec;
		pperson.get = 21 * 3600;
		if (pperson.come >= 21 * 3600) continue;
		pperson.play = play > 120 ? 120 * 60 : play * 60;
		pperson.isvip = (flag == 1 ? true : false);
		vec.push_back(pperson);
	}
	scanf("%d %d", &k,&p);
	tables.resize(k + 1);
	for (int i = 0; i < p; i++) {
		int temp;
		scanf("%d", &temp);
		tables[temp].isvip = true;
	}
	sort(vec.begin(), vec.end(), cmp);
	int i = 0;
	int vipid = getVIP(-1);
	while (i < vec.size()) {
		int min = 999999999, index = -1;
		for (int j = 1; j < tables.size(); j++) {
			if (tables[j].present < min) {
				min = tables[j].present;
				index = j;
			}
		}
		if (tables[index].present >= 21 * 3600) break;
		if (vec[i].isvip  == true && i < vipid) {
			i++;
			continue;
		}
		if (tables[index].isvip == true) {//会员桌子
			//先检查会员 插队
			if (vec[i].isvip == true) {//队头就是会员
				alloac(i, index);
				vipid = getVIP(vipid);
				i++;
			}
			else {
				if (vipid < vec.size() && vec[vipid].come <= tables[index].present) {//看看队里有没有会员
					alloac(vipid, index);
					vipid = getVIP(vipid);
				}
				else {//没有会员
					alloac(i, index);
					i++;
				}
			}
		}
		else {//普通桌子
			int mintemp = 999999999; int tempindex = -1;
			if (vec[i].isvip == true) {//第一个人是VIP
				for (int k = 1; k < tables.size(); k++) {
					if (tables[k].isvip && tables[k].present < mintemp) {
						mintemp = tables[k].present;
						tempindex = k;
					}
				}
				if (tempindex != -1 && tables[tempindex].present <= vec[i].come) {//有会员桌子 插队
					alloac(i, tempindex);
					vipid = getVIP(vipid);
					i++;
				}
				else {//没有会员桌子 那就跟普通人一样
					alloac(i, index);
					vipid = getVIP(vipid);
					i++;
				}
			}
			else {//普通人
				alloac(i, index);
				i++;
			}
		}
	}
	sort(res.begin(), res.end(), cmp2);
	for (int i = 0; i < res.size(); i++) {
		printf("%02d:%02d:%02d ", res[i].come / 3600, res[i].come % 3600 / 60, res[i].come % 60);
		printf("%02d:%02d:%02d %.0f\n", res[i].get / 3600, res[i].get % 3600 / 60, res[i].get % 60,round((res[i].get - res[i].come)/60.0));
		
	}
	printf("%d", tables[1].num);
	for (int i = 2; i < tables.size(); i++)
		printf(" %d", tables[i].num);
	return 0;
}
这里是其他方案,注意:不是满分的,但是是正确的,有多个超时点
#include<iostream>
#include<vector>
#include<algorithm>
#include<math.h>
using namespace std;
struct person{
public:
	int id,come, play, isvip, hour, min, sec, wait, get;//isvip为1表示会员
	bool isser = false;//初始化为未服务
}temperson;
struct table{
public:
	bool isvip = false;//初始化为普通桌子
	int present = 8 * 3600,num = 0;
}temptable;
bool cmp(person p1, person p2) {
	return p1.come < p2.come;
}
bool cmp3(person n1, person n2) {
	return n1.get < n2.get;
}
int n, k, m;
vector<table> tables;
vector<person> vec;
vector<person> res;
void alloact(int p,int t) {
	vec[p].get = vec[p].come < tables[t].present ? tables[t].present : vec[p].come;
	vec[p].wait = vec[p].get - vec[p].come;
	vec[p].isser = true;
	if (vec[p].get < 21 * 3600) {
		tables[t].present = vec[p].come + vec[p].play;
		tables[t].num++;
		res.push_back(vec[p]);
	}
}
int main() {
	scanf("%d", &n);
	vec.resize(n);
	for (int i = 0; i < n; i++) {//读人
		scanf("%d:%d:%d %d %d", &vec[i].hour, &vec[i].min, &vec[i].sec, &vec[i].play, &vec[i].isvip);
		vec[i].come = vec[i].sec + vec[i].min * 60 + vec[i].hour * 3600;
		vec[i].play = vec[i].play <= 120 ? vec[i].play * 60 : 120 * 60;
	}
	scanf("%d %d", &k, &m);
	tables.resize(k+1);
	for (int i = 0; i < m; i++) {//执行m次,读取并统计会员桌子
		int temp;//桌子id
		scanf("%d", &temp);
		tables[temp].isvip = true;
	}
	int count = 0;//计数器。统计处理人数 当到最后一个人时说明处理完了
	sort(vec.begin(), vec.end(), cmp);
	for (int i = 0; i < vec.size(); i++) {
		vec[i].id = i;
	}
	int pre = -1;
	while (count <= vec.size() - 1) {
		//选出最早服务完的桌子
		int index = -1;
		int min = 21*3600;//指向最早服务完的这个桌子,而且保证如果时间相同一定指向编号最小的那一个
		for (int i = 1; i < k + 1; i++) {
			if (tables[i].present < min) {
				index = i;
				min = tables[i].present;
			}
		}
		if (index == -1) //说明九点之前任何桌子都没有服务完,本来就要加班了 还有人要来 果断拒绝
			break;
		//将当前时间到来之前的人全部放进来
		vector<person> temp;
		int vipid = -1;//指向第一个VIP的下标
		for (int i = pre + 1; i < vec.size(); i++) {
			if (!vec[i].isser && vec[i].come <= tables[index].present) {
				temp.push_back(vec[i]);
				if (vec[i].isvip == 1 && vipid == -1) {
					vipid = i;
				}
			}
			else if (vec[i].come > tables[index].present) {
				break;
			}
		}
		if (temp.size() == 0) {//生意不好,没人在等
			//找下一个人
			for (int i = pre + 1; i < vec.size(); i++) {
				if (!vec[i].isser) {
					for (int j = 1; j < k + 1;j++) {
						if(tables[j].present < vec[i].come)
							tables[j].present = vec[i].come;
					}
					break;
				}
			}
			continue;
		}
		if (tables[index].isvip) {//如果这个桌子是会员桌子
			if (vipid != -1) {//有会员在等待
				alloact(vipid, index);//直接把会员桌子分配给他
				count++;
			}
			else {//没有会员就给第一个人
				alloact(temp[0].id, index);
				pre = temp[0].id;
				count++;
			}
		}
		else {//如果是普通桌子
			if (vipid != -1) {
				//一定要保障会员的权益,尽管这个桌子是普通桌子,要看一看是不是同时有得出空来的会员桌子
				for (int i = 1; i < k + 1; i++) {
					if (tables[i].present == tables[index].present && tables[i].isvip) {
						alloact(vipid, i);
						count++;
						break;
					}
				}
			}
			else {
				alloact(temp[0].id, index);//分配给第一个人
				pre = temp[0].id;
				count++;
			}
		}
	}
	sort(res.begin(), res.end(), cmp3);
	for (int i = 0; i < res.size(); i++) {
		int a = res[i].get / 3600;
		int b = (res[i].get - a * 3600) / 60;
		int c = res[i].get - (a * 3600 + b * 60);
		printf("%02d:%02d:%02d %02d:%02d:%02d %.0f\n", res[i].hour, res[i].min, res[i].sec, a, b, c, round(res[i].wait / 60.0));
	}
	printf("%d", tables[1].num);
	for (int i = 2; i < tables.size(); i++) {
		printf(" %d", tables[i].num);
	}
	return 0;
}

总结

对于非紧凑的排队问题,同样只关心队头元素,没有特殊情况就不关心后面的,一定是每次只处理队头元素, 特殊情况顶多让后面的插个队,不可以同时将多个元素划分到一起,一方面处理难度加大,另一方面耗时增加。 核心思想是让桌子/柜台去选人,另外一定要注意present = get + play 这一句是提醒我自己的……手动狗头

???

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
The following is the data that you can add to your input file (as an example). Notice that the first line is going to be a line representing your own hobbies. In my case, it is the Vitaly,table tennis,chess,hacking line. Your goal is to create a class called Student. Every Student will contain a name (String) and an ArrayList<String> storing hobbies. Then, you will add all those students from the file into an ArrayList<Student>, with each Student having a separate name and ArrayList of hobbies. Here is an example file containing students (the first line will always represent yourself). NOTE: eventually, we will have a different file containing all our real names and hobbies so that we could find out with how many people each of us share the same hobby. Vitaly,table tennis,chess,hacking Sean,cooking,guitar,rainbow six Nolan,gym,piano,reading,video games Jack,cooking,swimming,music Ray,piano,video games,volleyball Emily,crochet,drawing,gardening,tuba,violin Hudson,anime,video games,trumpet Matt,piano,Reading,video games,traveling Alex,swimming,video games,saxophone Roman,piano,dancing,art Teddy,chess,lifting,swimming Sarah,baking,reading,singing,theatre Maya,violin,knitting,reading,billiards Amy,art,gaming,guitar,table tennis Daniel,video games,tennis,soccer,biking,trumpet Derek,cooking,flute,gaming,swimming,table tennis Daisey,video games,guitar,cleaning,drawing,animated shows,reading,shopping Lily,flute,ocarina,video games,baking Stella,roller skating,sudoku,watching baseball,harp Sophie,viola,ukulele,piano,video games
06-10
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值