PAT甲级题分类汇编——线性
本文为PAT甲级分类汇编系列文章。
线性类,指线性时间复杂度可以完成的题。在1051到1100中,有7道:
题号 | 标题 | 分数 | 大意 | 时间 |
1054 | The Dominant Color | 20 | 寻找出现最多的数 | 200ms |
1061 | Dating | 20 | 寻找字符串中相同字符 | 200ms |
1071 | Speech Patterns | 25 | 寻找出现最多的单词 | 300ms |
1077 | Kuchiguse | 20 | 字符串共同后缀 | 150ms |
1082 | Read Number in Chinese | 25 | 中文读数 | 400ms |
1084 | Broken Keyboard | 20 | 比较两序列的差异 | 200ms |
1095 | Cars on Campus | 30 | 模拟车辆进出 | 300ms |
可以看到线性题一般分数不高,一般只有模拟事件的题会出30分,但也不难。
这种题一般一看就会做(最大子列和除外),难度一般在细节处理(所有PAT题都是)和时间常数上。
关于细节处理,分数低的题,本来就简单,做的时候容易想起一些细节问题,在第一次提交之前就处理好了。
关于时间限制,其他题时间限制一般都是400ms,但线性类时间更严格的比较多,这时候就要小心常数了。
仔细阅读了一下7道题,决定做1054、1071、1082和1095一共4道。
1054:
放在线性分类下,就要用线性方法做。用 std::map 做起来一看就很简单,为了挑战自己,换一种方法。
大致思路是每个维度(颜色)建立一个散列表,表中存放指向下一个维度的散列表的指针,最后一个维度存放数据。
代码如下:
#include <iostream>
#include <vector>
#include <memory> template <typename T>
using Pointer_vector = std::shared_ptr<std::vector<T>>; int main()
{
int m, n, total;
std::cin >> m >> n;
total = m * n;
using Blue = int;
using Green = Pointer_vector<Blue>;
using Red = Pointer_vector<Green>;
std::vector<Red> data();
for (int cnt = ; cnt != total; ++cnt)
{
int color;
std::cin >> color;
int red = color >> ;
int green = (color >> ) % ;
int blue = color % ;
auto& red_data = data[red];
if (!red_data)
red_data = Red(new std::vector<Green>());
auto& green_data = (*red_data)[green];
if (!green_data)
green_data = Green(new std::vector<Blue>());
auto& blue_data = (*green_data)[blue];
++blue_data;
}
try
{
for (int r = ; r != ; ++r)
if (data[r])
for (int g = ; g != ; ++g)
if ((*data[r])[g])
for (int b = ; b != ; ++b)
if ((*(*data[r])[g])[b] > total / )
throw (r << ) + (g << ) + b;
}
catch (int res)
{
std::cout << res;
}
}
对于多重循环要 break 的算法我一般用异常来做流控。这是一种备受争议的做法,因此我又想着用输出和 return 替换掉原来的 throw 语句,没想到竟然超时了!加了优化也没用。
不是说 try block会导致性能下降的吗?为什么加了异常处理以后性能反而提高?
想不通。我又用 std::map 实现了一个算法:
#include <iostream>
#include <map> #pragma GCC optimize(O3) int main()
{
int m, n, total;
std::cin >> m >> n;
total = m * n;
std::map<int, int> map;
for (int cnt = ; cnt != total; ++cnt)
{
int color;
std::cin >> color;
++map[color];
}
for (const auto& pair : map)
if (pair.second > total / )
{
std::cout << pair.first;
return ;
}
return ;
}
一开始当然是没有 #pragma 那一行的,但是超时了,后来加上了才AC。
这道题告诉我们,线性题对时间要求真的很高。或许用 scanf 代替 std::cin 能让原本超时的AC吧,我没试过。
1071:
字符串的线性处理、std::map 的使用,这道题难度就这么多了。代码如下:
#include <iostream>
#include <string>
#include <map>
#include <cctype> int main()
{
std::map<std::string, int> words;
std::string string;
while ()
{
char c = std::cin.get();
if (std::isalnum(c))
string.push_back(std::tolower(c));
else
if (!string.empty())
{
++words[string];
string.clear();
}
if (c == '\n')
break;
}
auto max = words.cbegin();
for (auto iter = words.cbegin(); iter != words.cend(); ++iter)
if (iter->second > max->second)
max = iter;
std::cout << max->first << ' ' << max->second << std::endl;
}
值得杠一杠的是这道题是不是线性。
首先,输入是O(n),输出是O(1),都在线性范围内。
假设一共有a种单词,对 std::map 的操作是O(a·loga);a种单词连起来的最小长度是O(a·loga),n一定大于这个长度,因此这道题是线性的。
1082:
呵,这道题就是在考数学。但我没有把它放到数学那一类中,因为这是小学数学。
一开始觉得很难,但只要把10000以内的数字怎么读搞清楚了,这道题就差不多了。代码如下:
#include <iostream>
#include <string>
#include <vector> std::vector<std::string> data; const char pinyin[][] =
{
"ling",
"yi",
"er",
"san",
"si",
"wu",
"liu",
"qi",
"ba",
"jiu"
}; void chinese(int num)
{
int qian = num / ;
num %= ;
int bai = num / ;
num %= ;
int shi = num / ;
int ge = num % ;
int digit = ;
if (qian)
{
digit = ;
data.push_back(pinyin[qian]);
data.push_back("Qian");
}
if (bai)
{
digit = ;
data.push_back(pinyin[bai]);
data.push_back("Bai");
}
if (shi)
{
if (digit > )
data.push_back(pinyin[]);
digit = ;
data.push_back(pinyin[shi]);
data.push_back("Shi");
}
if (ge)
{
if (digit > )
data.push_back(pinyin[]);
data.push_back(pinyin[ge]);
}
} int main()
{
int num;
std::cin >> num;
if (num == )
{
std::cout << pinyin[];
return ;
}
if (num < )
{
num = -num;
data.push_back("Fu");
}
int yi = num / ;
int wan = num % / ;
int ge = num % ;
int seg = ;
if (yi)
{
seg = ;
data.push_back(pinyin[yi]);
data.push_back("Yi");
}
if (wan)
{
if (wan < && seg > )
data.push_back(pinyin[]);
seg = ;
chinese(wan);
data.push_back("Wan");
}
if (ge)
{
if (ge < && seg > )
data.push_back(pinyin[]);
chinese(ge);
}
int end = data.size() - ;
for (int i = ; i != end; ++i)
std::cout << data[i] << ' ';
std::cout << data[end];
}
这是我第一次用拼音来命名变量。我也不想这样,但谁让这道题中文背景这么明显呢?
1095:
这道题要求模拟车辆进出校园的过程,这类模拟一个过程的题目,我称之为模拟类题。
模拟类题的一个通用方法就是以时间为变量循环,但是这道题写着写着就用上另一种方法了,就是以对象为变量循环,这里的对象就是车辆进出记录。
题目逻辑比较复杂,大致可以分为3个步骤:
第一步,读取所有记录,并按时间顺序排序,然后按记录类型配对;
第二步,读取查询的时间,并模拟车辆进出的过程,这是模拟类题的核心(但在本题中占的比例不大);
第三步,找出最长的停车时间,并输出对应的车牌号(老司机开车!)。
梳理完这堆逻辑以后就直接上代码吧:
#include <iostream>
#include <iomanip>
#include <string>
#include <vector>
#include <map>
#include <algorithm> int read_time()
{
int res, temp;
std::cin >> temp;
res = temp * ;
std::cin.get();
std::cin >> temp;
res += temp * ;
std::cin.get();
std::cin >> temp;
res += temp;
return res;
} void print_time(int time)
{
std::cout << std::setfill('');
std::cout << std::setw() << time / << ':';
time %= ;
std::cout << std::setw() << time / << ':';
std::cout << std::setw() << time % ;
} enum class Status
{
in, out
}; struct Record
{
std::string plate;
int time;
Status status;
bool paired = false;
int parking;
}; int main()
{
int n, k;
std::cin >> n >> k;
std::vector<Record> records(n);
for (auto& r : records)
{
std::cin >> r.plate;
r.time = read_time();
std::string str;
std::cin >> str;
r.status = str == "in" ? Status::in : Status::out;
}
std::sort(records.begin(), records.end(), [](const Record& _lhs, const Record& _rhs) {
return _lhs.time < _rhs.time;
});
for (auto rec = records.begin(); rec != records.end(); ++rec)
if (rec->status == Status::in)
for (auto iter = rec + ; iter != records.end(); ++iter)
if (iter->plate == rec->plate && iter->status == Status::in)
break;
else if (iter->plate == rec->plate && iter->status == Status::out)
{
rec->paired = iter->paired = true;
rec->parking = iter->time - rec->time;
break;
} auto iter = records.begin();
int count = ;
for (int cnt = ; cnt != k; ++cnt)
{
int time = read_time();
for (; iter != records.end() && iter->time <= time; ++iter)
if (iter->paired && iter->status == Status::in)
++count;
else if (iter->paired && iter->status == Status::out)
--count;
std::cout << count << std::endl;
} std::map<std::string, int> parking;
for (const auto& rec : records)
if (rec.paired && rec.status == Status::in)
parking[rec.plate] += rec.parking;
int longest = ;
for (const auto& car : parking)
if (car.second > longest)
longest = car.second;
for (const auto& car : parking)
if (car.second == longest)
std::cout << car.first << ' ';
print_time(longest);
}
这道题放在线性类,是因为配对和模拟的算法都是线性的(其实是因为我看到它是模拟类就把它分给线性类了)。
总之,线性类题目难度不高,但坑不少,不仅有各种边界数据,还有卡时间常数的。
PAT甲级题分类汇编——线性的更多相关文章
- PAT甲级题分类汇编——杂项
本文为PAT甲级分类汇编系列文章. 集合.散列.数学.算法,这几类的题目都比较少,放到一起讲. 题号 标题 分数 大意 类型 1063 Set Similarity 25 集合相似度 集合 1067 ...
- PAT甲级题分类汇编——图
本文为PAT甲级分类汇编系列文章. 图,就是层序遍历和Dijkstra这一套,#include<queue> 是必须的. 题号 标题 分数 大意 时间 1072 Gas Station 3 ...
- PAT甲级题分类汇编——理论
本文为PAT甲级分类汇编系列文章. 理论这一类,是让我觉得特别尴尬的题,纯粹是为了考数据结构而考数据结构.看那Author一栏清一色的某老师,就知道教数据结构的老师的思路就是和别人不一样. 题号 标题 ...
- PAT甲级题分类汇编——树
本文为PAT甲级分类汇编系列文章. AVL树好难!(其实还好啦~) 我本来想着今天应该做不完树了,没想到电脑里有一份讲义,PPT和源代码都有,就一遍复习一遍抄码了一遍,更没想到的是编译一遍通过,再没想 ...
- PAT甲级题分类汇编——排序
本文为PAT甲级分类汇编系列文章. 排序题,就是以排序算法为主的题.纯排序,用 std::sort 就能解决的那种,20分都算不上,只能放在乙级,甲级的排序题要么是排序的规则复杂,要么是排完序还要做点 ...
- PAT甲级题分类汇编——计算
本文为PAT甲级分类汇编系列文章. 计算类,指以数学运算为主或为背景的题. 题号 标题 分数 大意 1058 A+B in Hogwarts 20 特殊进制加法 1059 Prime Factors ...
- PAT甲级题分类汇编——序言
今天开个坑,分类整理PAT甲级题目(https://pintia.cn/problem-sets/994805342720868352/problems/type/7)中1051~1100部分.语言是 ...
- 【转载】【PAT】PAT甲级题型分类整理
最短路径 Emergency (25)-PAT甲级真题(Dijkstra算法) Public Bike Management (30)-PAT甲级真题(Dijkstra + DFS) Travel P ...
- PAT甲级题解分类byZlc
专题一 字符串处理 A1001 Format(20) #include<cstdio> int main () { ]; int a,b,sum; scanf ("%d %d& ...
随机推荐
- 一起入门Python2之python的安装及初识
鉴于论坛需要持续更新文章才能更好的保证论坛的访问量和质量,以及论坛的发展.承蒙前辈的信任和支持,我就教大家python第二课,教的不好请多担待和指正. 我们先讲python的安装方法: 方法链接:ht ...
- windows下安装node【转】
windows下安装node报错2503.2502 windows下命令行安装,首先使用管理员权限获取cmd msiexec /package node-v10.16.0-x64.msi 根据提示一步 ...
- 算法习题---4-8特别困的学生(UVa12108)
一:题目 课堂上有n个学生(n<=),每个学生上课都会出现一个“清醒-睡眠”周期,其中第i个学生学习Ai分钟后睡眠Bi分钟,依次重复.其中在从清醒到睡眠时有一个条件:只有到全班睡眠人数大于清醒人 ...
- 【Git】 Git安装及配置
Git是一个开源的分布式版本控制系统,可以有效.高速的处理从很小到非常大的项目版本管理.而国外的GitHub和国内的Coding都是项目的托管平台. 本例使用环境:Linux环境(CentOS 7.4 ...
- Qt编写自定义控件64-垂直时间轴
一.前言 垂直时间轴控件,主要用来描述企业发展历程大事件,或者软件版本迭代历史等,通过时间节点和事件描述来直观的展示发展的过程,一般在web网页或者app中经常看到此类控件,尤其是公司的官网关于公司部 ...
- Linux系统调优——CPU(一)
(1).系统调优思路 性能优化就是找到系统处理中的瓶颈以及去除这些的过程,性能优化其实是对OS 各子系统达到一种平衡的定义.具体步骤如下: 1. 系统的运行状况: CPU -> MEM -& ...
- C++高精度整数加减乘除模板
其中高精度乘法通过了POJ2389,其他没有测过,不过应该是没有问题的. 其中高精度除法返回一对string,分别表示商和余数. 代码: #include <bits/stdc++.h> ...
- MySQL普通索引性能试验
首先使用如下node.js脚本创建两张表,并为这两张表各自生成10000条数据: var fs = require('fs'); var nameS = "赵钱孙李周吴郑王冯陈褚卫蒋沈韩杨朱 ...
- anywhere随启随用的静态文件服务器
手机移动端调试,也可以使用anywhere anywhere -p 8080 指定端口 anywhere -s 保持浏览器关闭 anywhere -h localhost -p 8080 通过主机名 ...
- 12点睛Spring4.1-Spring Aware
12.1 Aware 我们设计的准则是解耦,这就意味着我们不能对Spring的IoC容器有直接的依赖,但是我们还是想我们的bean能识别容器的资源; 使用aware能让我们在应用的任意位置获得spri ...