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你已是一個大孩子了,要學會本身解決問題,別老是讓我寫算法。關鍵問題是你還老給我出難題。

相關文章
相關標籤/搜索