PAT甲级题分类汇编——杂项

本文为PAT甲级分类汇编系列文章。html

 

集合、散列、数学、算法,这几类的题目都比较少,放到一块儿讲。ios

题号 标题 分数 大意 类型
1063 Set Similarity 25 集合类似度 集合
1067 Sort with Swap(0, i) 25 经过与0号元素交换来排序 数学
1068 Find More Coins 30 子集和问题 算法
1070 Mooncake 25 背包问题 算法
1078 Hashing 25 散列 散列
1085 Perfect Sequence 25 符合约束的最大数列长度 集合
1092 To Buy or Not to Buy 20 判断子集 集合
1093 Count PAT's 25 数子串 数学

1063就是 std::set 的使用,1092更水,这两道不作了。算法

 

集合-1085:数组

集合,表示方法多种多样,简单的有用数组表示的并查集,复杂的有基于红黑树的 std::set 。数据结构

但我也不知道当时整理的时候怎么把这道题分到集合里来了,但还真挺难说这道题怎么分类的。题目要求从输入数据中找出最大的子列(不必定连续)知足最大值不超过最小值的给定倍数。函数

实现方法比较简单,先排序,而后对于每一个数,找到第一个超过它的倍数的数,迭代器相减一下就是长度。查找要用二分不能用线性,会超时。oop

 1 #include <iostream>
 2 #include <vector>
 3 #include <algorithm>
 4 
 5 int main()
 6 {
 7     long num, para;
 8     std::cin >> num >> para;
 9     std::vector<long> data(num);
10     for (auto& i : data)
11         std::cin >> i;
12     std::sort(data.begin(), data.end());
13     int max = 0;
14     for (int i = 0; i != num; ++i)
15     {
16         if (i == 0 || data[i] != data[i - 1])
17         {
18             auto left = data.begin() + i;
19             auto right = data.end();
20             while (left < right)
21             {
22                 auto mid = left + (right - left) / 2;
23                 if (*mid > data[i] * para)
24                     right = mid;
25                 else
26                     left = mid + 1;
27             }
28             int length = right - data.begin() - i;
29             if (length > max)
30                 max = length;
31             if (right == data.end())
32                 break;
33         }
34     }
35     std::cout << max;
36 }

 

散列-1078:spa

纯理论地考察散列知识,用单向平方探测法处理冲突。没什么好说的,就是很标准的散列。数据结构那个题集中有一道hard version,那个真的难。.net

 1 #include <iostream>
 2 #include <vector>
 3 #include <cmath>
 4 
 5 bool is_prime(int _num)
 6 {
 7     if (_num == 1)
 8         return false;
 9     if (_num > 2 && _num % 2 == 0)
10         return false;
11     int end = std::sqrt(_num);
12     for (int i = 3; i <= end; i += 2)
13         if (_num % i == 0)
14             return false;
15     return true;
16 }
17 
18 int main(int argc, char const *argv[])
19 {
20     int m;
21     std::cin >> m;
22     while (!is_prime(m))
23         ++m;
24     std::vector<bool> hash(m);
25     int n;
26     std::cin >> n;
27     for (int i = 0; i != n; ++i)
28     {
29         try
30         {
31             int t;
32             std::cin >> t;
33             for (int j = 0; j != m; ++j)
34             {
35                 int h = (t + j * j) % m;
36                 if (hash[h] == 0)
37                 {
38                     hash[h] = 1;
39                     if (i > 0)
40                         std::cout << ' ';
41                     std::cout << h;
42                     throw 0;
43                 }
44             }
45             if (i > 0)
46                 std::cout << ' ';
47             std::cout << '-';
48         }
49         catch (...)
50         {
51             ;
52         }
53     }
54     return 0;
55 }

 

数学-1067:code

乍一看,彻底没有思路。正常排序哪有这么玩的?显然,这道题不考排序,须要一点数学知识把问题转化一下。

对于一个序列,若是值与位置的集合相等,那么必定能够造成若干个环。这道题中须要考虑的是:一元环,即就在应该在的位置上的元素,以及0处于什么样的环中。

本身编几个数据试一下就会发现,须要交换的次数为:

当0处于0号位置时,序列长度+多元环个数-一元环个数(包括0);

当0号位置不是0时,序列长度+多元环个数-一元环个数-2。

 1 #include <iostream>
 2 #include <utility>
 3 
 4 int main(int argc, char const *argv[])
 5 {
 6     int n;
 7     std::cin >> n;
 8     std::pair<int, bool> data[n];
 9     int loop = 0;
10     int single = 0;
11     for (auto& i : data)
12         std::cin >> i.first;
13     for (int i = 0; i != n; ++i)
14     {
15         if (data[i].second)
16             continue;
17         data[i].second = 1;
18         if (data[i].first == i)
19             ++single;
20         else
21         {
22             ++loop;
23             int t = data[i].first;
24             while (!data[t].second)
25                 data[t].second = 1, t = data[t].first;
26         }
27     }
28     if (data[0].first == 0)
29         std::cout << n + loop - single;
30     else
31         std::cout << n + loop - single - 2;
32 
33     return 0;
34 }

 

数学-1093:

要求数“PAT”的个数。若是一个个数的话确定超时,毕竟题目中都说了数量很大要取模。

正常的算法也不麻烦。先从左向右遍历,数‘P’的数量,在遇到'A'时记录下来;再从右向左遍历,数'T'的数量,也在遇到'A'时记录下来;最后遍历,对每一个'A'计算刚才记录的数的乘积并累加。

#include <iostream>
#include <string>
#include <vector>

int main()
{
    std::string str;
    std::cin >> str;
    std::vector<int> count_p(str.size());
    std::vector<int> count_t(str.size());
    long count = 0;
    for (int i = 0; i != str.size(); ++i)
        if (str[i] == 'P')
            ++count;
        else if (str[i] == 'A')
            count_p[i] = count;
    count = 0;
    for (int i = str.size() - 1; i >= 0; --i)
        if (str[i] == 'T')
            ++count;
        else if (str[i] == 'A')
            count_t[i] = count;
    count = 0;
    for (int i = 0; i != str.size(); ++i)
        if (str[i] == 'A')
            count += count_p[i] * count_t[i];
    count %= 1000000007;
    std::cout << count;
}

 

算法-1070:

贪心算法,在数据结构的Dijkstra中提到过一句,没有系统讲过。可是,这道题的算法至关显然,对每种月饼的单价排序,从高到低选择便可。

 1 #include <iostream>
 2 #include <iomanip>
 3 #include <vector>
 4 #include <algorithm>
 5 #include <cmath>
 6 
 7 struct Mooncake
 8 {
 9     double weight;
10     double price;
11 };
12 
13 int main()
14 {
15     int num;
16     double demand;
17     std::cin >> num >> demand;
18     std::vector<Mooncake> product(num);
19     for (auto& p : product)
20         std::cin >> p.weight;
21     for (auto& p : product)
22         std::cin >> p.price;
23     std::sort(product.begin(), product.end(), [](const Mooncake& lhs, const Mooncake& rhs) {
24         return lhs.price / lhs.weight > rhs.price / rhs.weight;
25     });
26     double total = 0;
27     double profit = 0;
28     for (const auto& p : product)
29     {
30         double weight = p.weight > demand - total ? demand - total : p.weight;
31         total += weight;
32         profit += weight / p.weight * p.price;
33         if (std::abs(total - demand) < 0.001)
34             break;
35     }
36     std::cout.setf(std::ios::fixed);
37     std::cout << std::setprecision(2) << profit;
38 }

 

算法-1068:

这道题压轴,由于我不会。

由于不会,因此瞎写。先写了个递归实现,对每种硬币的数量枚举,其中递归调用函数。遇到有符合要求的组合时,就抛出异常,将硬币组合做为异常参数。结果两个样例居然都过了,然而提交的结果是4个accept、1个wrong、2个timeout。毕竟递归了,时间复杂度是指数量级的,不超时才怪呢。

想不出更好的算法,去查了答案。这道题是01背包问题,属于动态规划算法,我还没学到,难怪不会作。并且这道题是01背包问题的特殊版,子集和问题,重量和价值都是数的大小。相似的还有著名的两数和、三数和、四数和等。

核心算法是抄的,来自这篇博客

 1 #include <iostream>
 2 #include <vector>
 3 #include <algorithm>
 4 #include <functional>
 5 
 6 int main()
 7 {
 8     int num, target;
 9     std::cin >> num >> target;
10     std::vector<int> coin(num);
11     for (auto& i : coin)
12         std::cin >> i;
13     std::sort(coin.begin(), coin.end(), std::greater<int>());
14     std::vector<std::vector<bool>> select(num);
15     for (auto& v : select)
16         v.resize(target + 1);
17     std::vector<int> total(target + 1);
18     for (int i = 0; i != num; ++i)
19         for (int j = target; j >= coin[i]; --j)
20             if (total[j] <= total[j - coin[i]] + coin[i])
21             {
22                 select[i][j] = true;
23                 total[j] = total[j - coin[i]] + coin[i];
24             }
25     if (total[target] != target)
26     {
27         std::cout << "No Solution";
28         return 0;
29     }
30     int price = target;
31     int which = num - 1;
32     std::vector<int> found;
33     while (price)
34     {
35         if (select[which][price])
36         {
37             found.push_back(coin[which]);
38             price -= coin[which];
39         }
40         --which;
41     }
42     int count = 0;
43     for (int i : found)
44     {
45         if (count++)
46             std::cout << ' ';
47         std::cout << i;
48     }
49 }

最后还要说一句,Eva你已是一个大孩子了,要学会本身解决问题,别老是让我写算法。关键问题是你还老给我出难题。

相关文章
相关标签/搜索