PAT 1026

。。才得知这类题目不考了。。
那就简单记录一下代码。。这个有几个点通不过我也不知道为什么,逻辑上和柳神的没啥区别。。可能是我哪里边界出错了,不考模拟的话不再深究

#include<cstdio>
#include<vector>
#include<algorithm>
#include<cmath>
using namespace std;
const int K = 111;         //球桌数目
const int INF = 100000;    //无穷大
struct player {
	int arriveTime, startTime, serveTime;
	bool isVip;
}newplayer;
struct Table {
	int endTime, numsever;
	bool isVip;
}table[K];
vector<player> Player;

int converseTime(int h, int m, int s) {
	return h * 3600 + m * 60 + s;
}
//用到来时间排序用于数据的处理
bool cmpArriveTime(player a, player b) {
	return a.arriveTime < b.arriveTime;
}
//用开始时间排序,用于最后的输出
bool cmpStartTime(player a, player b) {
	return a.startTime < b.startTime;
}
//寻找下一个vip
int nextVipPlaer(int vipi) {
	vipi++;
	while (vipi < Player.size() && Player[vipi].isVip == false ) {
		vipi++;
	}
	return vipi;
}
//将pid号球员分配给tid号球桌
void allotTable(int pid, int tid) {
	if (Player[pid].arriveTime <= table[tid].endTime) {
		Player[pid].startTime = table[tid].endTime;
	}
	else {
		Player[pid].startTime = Player[pid].arriveTime;
	}
	table[tid].endTime = Player[pid].startTime + Player[pid].serveTime;
	table[tid].numsever++;
}

int main() {
	int n, k, m, VipTable;
	scanf_s("%d", &n);
	int stTime = converseTime(8, 0, 0);
	int endTime = converseTime(21, 0, 0);
	for (int i = 0; i < n; i++) {
		int h, m, s, playtime, isVip;
		scanf_s("%d:%d:%d %d %d", &h, &m, &s, &playtime, &isVip);
		newplayer.arriveTime = converseTime(h, m, s);
		newplayer.isVip = isVip;
		newplayer.startTime = endTime;
		//到达时间比结束时间晚,直接不予进队
		if (newplayer.arriveTime >= endTime) continue;
		//训练时间大于2h按照2h处理
		newplayer.serveTime = playtime <= 120 ? playtime * 60 : 7200;
		Player.push_back(newplayer);
	}
	scanf_s("%d %d", &k, &m);
	for (int i = 1; i <= k; i++) {
		table[i].endTime = stTime;
		table[i].numsever = table[i].isVip = 0;
	}
	for (int i = 0; i < m; i++) {
		scanf_s("%d", &VipTable);
		table[VipTable].isVip = 1;
	}
	sort(Player.begin(), Player.end(), cmpArriveTime);
	int i = 0, Vipi = -1; // 给vipi初值为 -1,防止第一个球员就是vip使得其被跳过
	Vipi = nextVipPlaer(Vipi);
	while (i < Player.size()) {
		//找最快结束的球桌
		int idx = -1, minEndTime = INF;
		for (int j = 1; j <= k; j++) {
			if (table[j].endTime < minEndTime) {
				idx = j;
				minEndTime = table[j].endTime;
			}
		}
		//最快结束的球桌都已经超过了时间,则关门
		//这步的目的是因为有可能球员来的时候没关门但是轮到他训练前关门了
		if (table[idx].endTime >= endTime) break;
		//这个vip球员已经被服务过了,直接跳过他
		if (Player[i].isVip == 1 && i < Vipi) {
			i++;
			continue;
		}
		//下面分别按照球桌是否是VIP,球员是否是VIP四种情况进行讨论
		if (table[idx].isVip == 1) {
			if (Player[i].isVip == 1) {  //球桌是vip, 球员也是vip
				allotTable(i, idx);
				if (Vipi == i) Vipi = nextVipPlaer(Vipi);    //当前就是vip球员,所以找下一个vip
				i++;
			}
			else {    //球桌是vip,球员不是vip
				//如果队列里有vip球员,则让他插队
				//注:Vipi这个数会永远存在,因为寻找到过程判断条件有一个是越界,所以在这里也要判断一下是否还有vip
				if (Vipi <Player.size() && Player[Vipi].arriveTime <= table[idx].endTime) {  
					allotTable(Vipi, idx);
					Vipi = nextVipPlaer(Vipi);
				}
				else {
					allotTable(i, idx);
					i++;
				}
			}
		}
		else {
			if (Player[i].isVip == 0) {   //球桌是vip桌,球员不是vip
				allotTable(i, idx);
				i++;
			}
			else {					//球桌不是vip,球员是vip
				//找到结束的vip桌
				int VipIdx = -1, minviptime = INF;
				for (int j = 1; j <= k; j++) {
					if (table[j].endTime < minviptime && table[j].isVip == 1) {
						minviptime = table[j].endTime;
						VipIdx = j;
					}
				}
				if (VipIdx != -1 && table[VipIdx].endTime <= Player[i].arriveTime) {
					//vip桌存在,且空闲时间比球员来得早(其实就是和普通桌一起空闲)
					//分配给vip球员
					allotTable(i, VipIdx);
					if (Vipi == i) Vipi = nextVipPlaer(Vipi);
					i++;
				}
				else {
					allotTable(i, idx);
					if (Vipi == i) Vipi = nextVipPlaer(Vipi);
					i++;

				}
			}
		}
	}
	sort(Player.begin(), Player.end(), cmpStartTime);
	for (int i = 0; i < Player.size() && Player[i].startTime < endTime; i++) {
		int ta = Player[i].arriveTime;
		int ts = Player[i].startTime;
		printf("%02d:%02d:%02d ", ta / 3600, ta % 3600 / 60, ta % 60);
		printf("%02d:%02d:%02d ", ts / 3600, ts % 3600 / 60, ts % 60);
		printf("%.0f\n", round(ts - ta) / 60.0);
	}
	for (int i = 1; i <= k; i++) {
		printf("%d", table[i].numsever);
		if (i < k) printf(" ");
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值