(PAT Advanced) 1056.Mice and Rice (队列的使用——比赛晋级淘汰模拟) C++

原题:https://pintia.cn/problem-sets/994805342720868352/problems/994805419468242944
在这里插入图片描述
题目大意

np为老鼠的体重,ng为每组最多的老鼠数量,给出每个老鼠的体重,和初始顺序。然后每ng个老鼠为一组,挑选每组中体重最大的老鼠晋级下一轮,然后剩下的未晋级的老鼠给予相同评级,即若当前晋级group只老鼠,则剩下老鼠的评级统一为group+1,直到所有老鼠评级完成,进行输出。

初步想法

由于需要一个数据结构,来存储老鼠,且每次需要从中挑选出晋级的老鼠晋级,之后依次循环进行比较。这里,我使用队列queue来存储初始老鼠,然后每ng个老鼠中挑选最重的老鼠进行晋级,晋级的结果是 将该老鼠编号压入队列尾部 方便下一次比较,每次用一个数组book来记录已晋级的老鼠,最后一轮之后,给没有晋级的老鼠进行评级,级别为晋级老鼠个数(队列长度)+1,直到队列元素只有一只老鼠时,它就是第一名,此时比赛停止。

/* 1056 Mice and Rice (25 分) */
// 队列来模拟排序晋级
#include<queue>
#include<vector>
#include<iostream>

using namespace std;

int main() {
	int DEBUG = 0;
	int np, ng;
	vector<int> weight;
	vector<int> rank;
	vector<int> book;
	queue<int> Q; // 模拟排序
	scanf("%d %d", &np, &ng);
	weight.resize(np);  rank.resize(np); book.resize(np);
	int i, index;
	for (i = 0; i < np; i++) scanf("%d", &weight[i]);
	for (i = 0; i < np; i++) {
		scanf("%d", &index);
		Q.push(index);
	}
	// 开始模拟排序晋级
	int len = Q.size(); // 表示上一轮队列的长度
	while (len != 1) {
		for (i = 0; i < len;) {
			int maxindex = i, maxweight = -1;
			int j;
			for (j = i; j < i + ng && j < len; j++) {
				index = Q.front(); Q.pop();
				if (DEBUG) printf("当前参与排名的老鼠%d\n", index);
				if (weight[index] > maxweight) {
					maxweight = weight[index];
					maxindex = index;
				}
			}
			if (DEBUG) printf("晋级选手%d\n", maxindex);
			book[maxindex] = 1; // 晋级
			Q.push(maxindex);   // 进队参与下一次比较
			i = j;
		}
		len = Q.size(); // 下一次比较的老鼠数,也是这一轮晋级的老鼠个数
		if (DEBUG) printf("本轮竞晋级完毕,排名为%d\n", len + 1);
		for (i = 0; i < np; i++) {
			if (book[i] == 0 && rank[i] == 0)  // 没晋级也没排名
				rank[i] = len + 1;
		}
		fill(book.begin(), book.end(), 0);
	}
	rank[Q.front()] = 1; // 排序第一名
	for (i = 0; i < np; i++) {
		printf("%d", rank[i]);
		i != np - 1 ? printf(" ") : printf("\n");
	}
	system("pause");
	return 0;
}

晋级顺序
上图是晋级示意图。

小优化

每次我们使用了数组book来记录晋级的老鼠,然后对其他老鼠进行评级,但是这样我们在每一轮排序前都要重置该数组,而且搜索未晋级和未评级的老鼠时要对数组进行全局搜索,因此我们可以使用其他方法来避免对该数组的使用。

即我们在每次对老鼠进行评级前,先计算出本轮未晋级老鼠的评级是多少,即假如队列中有n只老鼠,若n可被ng整除,则我们可以分为group = n / ng 组;若n不可被ng整除,则我们可以分成group = n/ng + 1组。此时,本次的评级为group+1,我们对该队列中的所有老鼠进行评级,同时,晋级的老鼠重新压入队列,这样,这些老鼠在之后会被重新进行评级,则我们不需要每次记录晋级老鼠了。

/* 1056 Mice and Rice (25 分) */
// 队列来模拟排序晋级
#include<queue>
#include<vector>
#include<iostream>

using namespace std;

int main() {
	int np, ng;
	vector<int> weight;
	vector<int> rank;
	queue<int> Q; // 模拟排序
	scanf("%d %d", &np, &ng);
	weight.resize(np);  rank.resize(np);
	int i, index;
	for (i = 0; i < np; i++) scanf("%d", &weight[i]);
	for (i = 0; i < np; i++) {
		scanf("%d", &index);
		Q.push(index);
	}
	// 开始模拟排序晋级
	int len = Q.size(); // 表示上一轮队列的长度
	while (len != 1) {
		int group = len / ng;
		if (len % ng != 0) group++;//本次晋级的老鼠
		for (i = 0; i < len;) {
			int maxindex = i, maxweight = -1;
			int j;
			for (j = i; j < i + ng && j < len; j++) {
				index = Q.front(); Q.pop();
				rank[index] = group + 1; // 对本轮的所有老鼠进行评级
				if (weight[index] > maxweight) {
					maxweight = weight[index];
					maxindex = index;
				}
			}
			Q.push(maxindex);   // 进队参与下一次比较
			i = j;
		}
		len = group;
	}
	rank[Q.front()] = 1; // 排序第一名
	for (i = 0; i < np; i++) {
		printf("%d", rank[i]);
		i != np - 1 ? printf(" ") : printf("\n");
	}
	system("pause");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值