PAT(乙级)2021年冬季考试答案

7-1 自动打包机

    一条哈密瓜自动打包流水线的工作程序是这样的:首先系统设定每箱哈密瓜应该有的总重量 W;然后传送带将一只只哈密瓜输送到一个自动称重设备上,根据称重结果进行以下操作:

  • 如果称上的总重量正好达到 W,则将称上的所有哈密瓜装箱送走;
  • 如果称上的总重量还不到 W,则将这只哈密瓜留在称上;
  • 如果称上的总重量超过了 W,则将这只哈密瓜放到一边暂不处理。

    本题就请你写个程序统计一下,究竟有多少只哈密瓜被装了多少箱?

输入格式:

    输入第一行给出两个正整数 N(≤1000)和 W(≤104),分别为传送带上哈密瓜的数量和每箱的规定重量(克)。随后一行给出 N 个正整数,是传送带上每只哈密瓜的重量,单果重量不超过 2000 克。假设传送带按照输入的顺序传送哈密瓜到称重设备。

输出格式:

    在一行中输出成功装箱的箱子数量和被装箱的哈密瓜的数量。数字间以 1 个空格分隔,行首尾不得有多余空格。

输入样例:

12 5000
2000 1500 1800 1000 1800 500 1900 1500 2000 1600 2000 2000

输出样例:

2 7

样例说明:

    第 1、2、4、6 只瓜正好一箱;第 7、8、10 只瓜正好一箱。

AC代码:

    注意:没装成一箱的哈密瓜不能算入被装箱的哈密瓜的数量。使用cntG预算出哈密瓜的数量,等装箱之后将其并入finalCnt中。cntbox计算箱子的数量根据题目要求计算即可。

#include "bits/stdc++.h"
using namespace std;

int main()
{
	int N, W;
	cin >> N >> W;
	int sum = 0;
	int cntbox = 0;
	int cntG = 0;
	int finalCnt = 0;
	for(int i = 0; i < N; i++)
	{
		int num;
		cin >> num;
		if(sum + num < W){
			sum += num;
			cntG++;
		}else if(sum + num == W){
			cntbox++;
			finalCnt += (cntG + 1);
			cntG = 0;
			sum = 0;
		}
	}
	cout << cntbox << " " << finalCnt << endl;
	return 0;
}

7-2 用药统计

    上图是知乎上的一个问题:“在计算机中,有没有那种把很多病历导入,然后用计算机统计哪种药用的多的语言。?”虽然这个问题有错别字和标点符号错误,但好心的你还是帮他做一个吧 —— 给定 N 位病人病历中的用药纪录,请你统计被用的最多的那种药,并且列出其治疗过的疾病。

输入格式:

    输入第一行给出正整数 N(≤104),为病历个数。随后 N 行,每行给出一条病历信息,格式如下:

疾病编号 K 药物编号1 药物编号2 …… 药物编号K

    其中疾病编号是一个 4 位数字,题目保证每条病历中的疾病编号都是不同的;K 是不超过 10 的正整数,为该疾病用药的种类数;药物编号是一个以 MD 开头、后面跟 5 位数字的字符串。题目保证在一条病历中没有重复的药物编号。

输出格式:

    首先在第一行输出被用的最多的那种药的编号,及其被用的次数,其间以一个空格分隔。如果有并列,则只输出编号最小(即 MD 后那串数字最小)的那个。随后按照输入的顺序输出该药物治疗过的疾病的编号,每行一个。

输入样例:

5
0012 3 MD10031 MD99132 MD42107
1024 2 MD99132 MD34821
2048 2 MD27845 MD10031
0149 2 MD34821 MD55802
0035 3 MD55802 MD99132 MD10031

输出样例:

MD10031 3
0012
2048
0035

AC代码:

    定义结构体数组 Virus v[n] 记录各种疾病,在结构体中定义疾病id和可治疗药物的集合sets。

    通过map集合的键值对关系记录使用最多的药的编号以及被用次数,在此期间计算出用药最多的次数。之后遍历集合,得到使用最多次的药的名称和次数,打印。

    最后遍历结构体数组,凡是在集合中能够查到当前药名maxMed的,打印疾病id,使用%04d保证四位id的输出。

#include "bits/stdc++.h"
using namespace std;

map<string, int> cntmp;

struct Virus{
	int id;
	set<string> sets;
};

Virus v[10005];

int main()
{
	int N;
	cin >> N;
	int maxValue = 0;
	for(int i = 0; i < N ; i++)
	{
		set<string> st;
		int id, K;
		cin >> id >> K;
		for(int j = 0; j < K; j++)
		{
			string med;
			cin >> med;
			if(cntmp.find(med) == cntmp.end()){
				cntmp[med] = 1;
			}else{
				cntmp[med]++;
			}
			if(cntmp[med] > maxValue){
				maxValue = cntmp[med];
			}
			st.insert(med);
		}
		v[i].id = id;
		v[i].sets = st;
	}
	string maxMed = "";
	for(map<string, int>::iterator it = cntmp.begin(); it != cntmp.end(); it++){
		if(it->second == maxValue){
			maxMed = it->first;
			cout << it->first << " " << it->second << endl;
			break;
		}
	}
	for(int i = 0; i < N; i++){
		if(v[i].sets.find(maxMed) != v[i].sets.end()){
			printf("%04d\n", v[i].id);
		}
	}
	return 0;
}

7-3 五彩斑斓的黑

    上面这张图是一条五彩斑斓的黑蛇。我们从头到尾扫描了蛇的身体,将获得的颜色 RGB 值顺序列出。下面就请你统计一下,这条五彩斑斓的黑蛇身上共有多少种不同的颜色,并且将每种不同的颜色按照它们第一次被扫描到的顺序输出。

输入格式:

    输入首先在第一行中给出一个正整数 N(≤105),随后一行给出 N 个颜色的 RGB 值,每个颜色的格式为 RRR.GGG.BBB,其中 RRRGGGBBB 的值在区间 [0,255] 内。相邻颜色间以 1 个空格分隔。

输出格式:

    在第一行中输出不同颜色的数量。第二行按照输入相同的格式、每种颜色第一次被扫描到的顺序输出各种不同颜色的值。相邻颜色间以 1 个空格分隔,行首尾不得有多余空格。

输入样例:

7
000.000.000 255.255.255 000.000.000 000.123.072 000.000.000 000.000.000 000.123.072

输出样例:

3
000.000.000 255.255.255 000.123.072

AC代码:

    利用set集合(基于红黑树实现高效查找)【补:利用unordered_set底层基于哈希表也很快】的去重效果判断当前元素是否已经存在于集合中,不存在则添加与vector容器中,最后遍历vector容器即可。

#include "bits/stdc++.h"
using namespace std;

vector<string> v;
set<string> st;

int main()
{
	int N;
	cin >> N;
	for(int i = 0; i < N; i++)
	{
		string color;
		cin >> color;
		if(st.find(color) == st.end()){
			st.insert(color);
			v.push_back(color);
		}
	}
	cout << v.size() << endl;
	for(int i = 0; i < v.size(); i++)
	{
		if(i)	cout << " ";
		cout << v[i];
	}
	return 0;
}

7-4 假新闻

    所有的新闻网站都是有自己立场的,只是有些能比较好地平衡事实与己见。“假新闻”是指为了误导大众对真实事件的理解而做的故意歪曲事实的报导。

    为了甄别新闻媒体是否属于假新闻媒体,我们可以注意观察若干不同的新闻网站对同一个重要事件的报导。甄别算法如下:

  • 首先选择 N 家新闻网站。
  • 对每一个重要的事件,扫描每家网站,用不同的整数表示报导的不同观点。
  • 定义网站 i 报导假新闻的可能性为 Fi​=ni​/N,其中 ni​ 是与网站 i 持不同观点的其它新闻网站数量。
  • 找出报导假新闻可能性最大的一家或几家网站。
  • 对每家网站,统计其成为最可能的假新闻网站的次数,最后找出次数最多的那家,就是在最多情况下最可能报导假新闻的网站。

输入格式:

    输入第一行给出两个正整数 N (≤ 10^4),为关注的新闻网站的数量;以及 M(≤100),为关注的重要事件的数量。随后 M 行,每行按以下格式给出各个新闻网站报导的观点:

    R1​ R2​ ... RN​

    其中 Ri​ 是区间 [−10^4,10^4] 内的整数,表示网站 i 的观点。

输出格式:

    在一行中输出在最多情况下最可能报导假新闻的网站的编号。题目保证解是唯一的。

输入样例:

4 6
4 2 7 7
1 1 1 3
2 9 9 5
-1 -1 -1 -1
-2 2 -2 2
1 1 3 4

输出样例:

4

样例说明:

    每次中大事件对应的 Fi​ 的值如下:

事件 1: 3/4 3/4 2/4 2/4 --> 1 和 2 疑似
事件 2: 1/4 1/4 1/4 3/4 --> 4 疑似
事件 3: 3/4 2/4 2/4 3/4 --> 1 和 4 疑似
事件 4: 0 0 0 0 --> 全都疑似
事件 5: 2/4 2/4 2/4 2/4 --> 全都疑似
事件 6: 2/4 2/4 3/4 3/4 --> 3 和 4 疑似

    所以只有 4 号网站有 5 次都被列为最大疑似,其它都只有 3 或 4 次。

AC代码:

    使用unordered_map记录每一次重要事件的意见与次数。已知题目可能性为 Fi​=ni​/N,此处的 “/N”都存在所以只需要比较分子部分ni就可以了。这里注意ni的概念: ni​ 是与网站 i 持不同观点的其它新闻网站数量。比如样例 “4 2 7 7”,第一个4是网站一的观点,剩下三个和网站一的观点都不相同所以n1 = 3(这个地方搞不清楚可能会出现测试点3答案错误)。在代码中使用posF表示ni,使用maxPos计算出最大的ni。

    现在得到了最大的ni即maxPos了(也就是与大多数网站意见相差最大的量--->即最有可能造假的情况),重复上述计算posF也就是ni,然后当计算的ni的值为maxPos的时候即表示本次该网站疑似假新闻,对应map中的值+1。

    最后使用迭代器遍历map,就能算出那唯一的(已知),最可能报导假新闻的网站的编号。

#include "bits/stdc++.h"
using namespace std;

map<int, int> mp;

int main()
{
	int N, M;
	cin >> N >> M;
	for(int i = 0; i < M; i++)
	{
		unordered_map<int, int> cntmp;
		vector<int> v;
		for(int j = 0; j < N; j++)
		{
			int R;
			cin >> R;
			v.push_back(R);
			if(cntmp.find(R) == cntmp.end()){
				cntmp[R] = 1;
			}else{
				cntmp[R]++;
			}
		}
		int maxPos = 0;
		for(int j = 0; j < N; j++)
		{
			int posF = N - cntmp[v[j]];
			if(posF > maxPos){
				maxPos = posF;
			}
		}
		for(int j = 0; j < N; j++)
		{
			int posF = N - cntmp[v[j]];
			if(posF == maxPos){
				if(mp.find(j + 1) == mp.end()){
					mp[j + 1] = 1;
				}else{
					mp[j + 1]++;
				}
			}
		}
	}
	int maxFack = 0;
	int Fackable = 0;
	for(map<int, int>::iterator it = mp.begin(); it != mp.end(); it++){
		if(it->second > maxFack){
			maxFack = it->second;
			Fackable = it->first;
		}
	}
	cout << Fackable << endl;
	return 0;
}

7-5 静态链表的秩

    静态链表是指将一个有 n 个结点的链表存放在一个有 n 个单元的数组里,每个数组单元包含一个整型的 data 域和一个 next 指针,指针里存的是链表下一个结点在数组里对应的单元下标。题目保证给出的链表是一个线性表,即除了第一个结点外,每个结点有其唯一的前驱结点;除了最后一个结点外,每个结点有其唯一的后继结点。

    你的任务是将这个链表上的结点顺序编号,即从第一个结点开始,从 1 到 n 给每个结点顺次编号 —— 这个编号就叫结点的“秩”。

输入格式:

    输入第一行给出一个正整数 n (≤ 10^5),为链表中的结点个数。第二行给出 n 个数字,第 i (i=0,⋯,n−1) 个数字对应数组第 i 个单元存储的 next(i) 的值,空指针用 −1 表示。数字间以空格分隔。

输出格式:

    在一行中输出 n 个数字,第 i (i=0,⋯,n−1) 个数字对应第 i 个单元存储的结点在链表中的秩。数字间以 1 个空格分开,行首尾不得有多余空格。

输入样例:

5
3 -1 4 1 0

输出样例:

3 5 1 4 2

样例说明:

    输入的链表为 2->4->0->3->1->空。于是第 0 个单元所存的结点的秩是 3,因为它是链表中的第 3 个结点;第 1 个单元所存的结点的秩是 5,因为它是链表中的第 5 个结点;以此类推。

反面教材:

    先说一个结论(教训):如果想通过遍历找链表头节点,用cnt记录链表节点个数,当cnt == n时找到了编号这种思路:

    如果你从前向后正着找:

    如果你从后向前倒着找:

    所以这道题,一定要用好:正难则反!!!

    所谓正难则反,链表头节点找起来费时间,每找一个节点都得从头遍历到尾。那我找尾结点也就是next(i) = -1的点这不就完事儿了!!!

AC代码:

    初始化cnt为n,使用map集合键值对逆向记录<next, pre>,对数组v[n]进行编号最后遍历打印即可,使用样例举个例子:

    初始化cnt = n = 5,

  • 当找到-1后,mp[-1] = 1 为当前 -1 所在的索引位置,v[mp[-1]] = v[1] = cnt = 5,cnt--;
  • 拿着 1 这个索引继续,mp[1] = 3,v[mp[1]] = v[3] = cnt = 4,cnt--;
  • 找到 3 这个索引继续,mp[3] = 0,v[mp[3]] = v[0] = cnt = 3,cnt--;
  • 找到 0 这个索引继续,mp[0] = 4,v[mp[0]] = v[4] = cnt = 2,cnt--;
  • 找到 4 这个索引继续,mp[4] = 2,v[mp[4]] = v[2] = cnt = 1,cnt--;
  • cnt == 0,退出while循环。

从0~n打印v[n],即 3 5 1 4 2 为样例输出。

#include "bits/stdc++.h"
using namespace std;

map<int, int> mp;

int main()
{
    int n;
    cin >> n;
    int res[n];
    for(int i = 0; i < n; i++)
    {
        int next;
        cin >> next;
        mp[next] = i;
    }
    map<int, int>::iterator it = mp.begin();
    while(it->first != -1){
        it++;
    }
    int cnt = n;
    int num = it->second;
    while(cnt > 0)
    {
        res[num] = cnt;
        cnt--;
        num = mp[num];
    }
    for(int i = 0; i < n; i++)
    {
        if(i)	cout << " ";
        cout << res[i];
    }
    return 0;
}
  • 28
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值