PAT甲級題分類彙編——線性

本文爲PAT甲級分類彙編系列文章。html

 

線性類,指線性時間複雜度能夠完成的題。在1051到1100中,有7道:ios

題號 標題 分數 大意 時間
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分,但也不難。git

這種題通常一看就會作(最大子列和除外),難度通常在細節處理(全部PAT題都是)和時間常數上。算法

關於細節處理,分數低的題,原本就簡單,作的時候容易想起一些細節問題,在第一次提交以前就處理好了。性能

關於時間限制,其餘題時間限制通常都是400ms,但線性類時間更嚴格的比較多,這時候就要當心常數了。優化

 

仔細閱讀了一下7道題,決定作1054107110821095一共4道。spa

 

1054:指針

放在線性分類下,就要用線性方法作。用 std::map 作起來一看就很簡單,爲了挑戰本身,換一種方法。code

大體思路是每一個維度(顏色)創建一個散列表,表中存放指向下一個維度的散列表的指針,最後一個維度存放數據。htm

代碼以下:

 1 #include <iostream>
 2 #include <vector>
 3 #include <memory>
 4 
 5 template <typename T>
 6 using Pointer_vector = std::shared_ptr<std::vector<T>>;
 7 
 8 int main()
 9 {
10     int m, n, total;
11     std::cin >> m >> n;
12     total = m * n;
13     using Blue = int;
14     using Green = Pointer_vector<Blue>;
15     using Red = Pointer_vector<Green>;
16     std::vector<Red> data(256);
17     for (int cnt = 0; cnt != total; ++cnt)
18     {
19         int color;
20         std::cin >> color;
21         int red = color >> 16;
22         int green = (color >> 8) % 256;
23         int blue = color % 256;
24         auto& red_data = data[red];
25         if (!red_data)
26             red_data = Red(new std::vector<Green>(256));
27         auto& green_data = (*red_data)[green];
28         if (!green_data)
29             green_data = Green(new std::vector<Blue>(256));
30         auto& blue_data = (*green_data)[blue];
31         ++blue_data;
32     }
33     try
34     {
35         for (int r = 0; r != 256; ++r)
36             if (data[r])
37                 for (int g = 0; g != 256; ++g)
38                     if ((*data[r])[g])
39                         for (int b = 0; b != 256; ++b)
40                             if ((*(*data[r])[g])[b] > total / 2)
41                                 throw (r << 16) + (g << 8) + b;
42     }
43     catch (int res)
44     {
45         std::cout << res;
46     }
47 }

對於多重循環要 break 的算法我通常用異常來作流控。這是一種備受爭議的作法,所以我又想着用輸出和 return 替換掉原來的 throw 語句,沒想到居然超時了!加了優化也沒用。

不是說 try block會致使性能降低的嗎?爲何加了異常處理之後性能反而提升?

想不通。我又用 std::map 實現了一個算法:

 1 #include <iostream>
 2 #include <map>
 3 
 4 #pragma GCC optimize(O3)
 5 
 6 int main()
 7 {
 8     int m, n, total;
 9     std::cin >> m >> n;
10     total = m * n;
11     std::map<int, int> map;
12     for (int cnt = 0; cnt != total; ++cnt)
13     {
14         int color;
15         std::cin >> color;
16         ++map[color];
17     }
18     for (const auto& pair : map)
19         if (pair.second > total / 2)
20         {
21             std::cout << pair.first;
22             return 0;
23         }
24     return 0;
25 }

一開始固然是沒有 #pragma 那一行的,可是超時了,後來加上了才AC。

 

這道題告訴咱們,線性題對時間要求真的很高。或許用 scanf 代替 std::cin 能讓本來超時的AC吧,我沒試過。

 

1071:

字符串的線性處理、std::map 的使用,這道題難度就這麼多了。代碼以下:

 1 #include <iostream>
 2 #include <string>
 3 #include <map>
 4 #include <cctype>
 5 
 6 int main()
 7 {
 8     std::map<std::string, int> words;
 9     std::string string;
10     while (1)
11     {
12         char c = std::cin.get();
13         if (std::isalnum(c))
14             string.push_back(std::tolower(c));
15         else
16             if (!string.empty())
17             {
18                 ++words[string];
19                 string.clear();
20             }
21         if (c == '\n')
22             break;
23     }
24     auto max = words.cbegin();
25     for (auto iter = words.cbegin(); iter != words.cend(); ++iter)
26         if (iter->second > max->second)
27             max = iter;
28     std::cout << max->first << ' ' << max->second << std::endl;
29 }

值得槓一槓的是這道題是否是線性。

首先,輸入是O(n),輸出是O(1),都在線性範圍內。

假設一共有a種單詞,對 std::map 的操做是O(a·loga);a種單詞連起來的最小長度是O(a·loga),n必定大於這個長度,所以這道題是線性的。

 

1082:

呵,這道題就是在考數學。但我沒有把它放到數學那一類中,由於這是小學數學。

一開始以爲很難,但只要把10000之內的數字怎麼讀搞清楚了,這道題就差很少了。代碼以下:

  1 #include <iostream>
  2 #include <string>
  3 #include <vector>
  4 
  5 std::vector<std::string> data;
  6 
  7 const char pinyin[10][5] =
  8 {
  9     "ling",
 10     "yi",
 11     "er",
 12     "san",
 13     "si",
 14     "wu",
 15     "liu",
 16     "qi",
 17     "ba",
 18     "jiu"
 19 };
 20 
 21 void chinese(int num)
 22 {
 23     int qian = num / 1000;
 24     num %= 1000;
 25     int bai = num / 100;
 26     num %= 100;
 27     int shi = num / 10;
 28     int ge = num % 10;
 29     int digit = 0;
 30     if (qian)
 31     {
 32         digit = 3;
 33         data.push_back(pinyin[qian]);
 34         data.push_back("Qian");
 35     }
 36     if (bai)
 37     {
 38         digit = 2;
 39         data.push_back(pinyin[bai]);
 40         data.push_back("Bai");
 41     }
 42     if (shi)
 43     {
 44         if (digit > 2)
 45             data.push_back(pinyin[0]);
 46         digit = 1;
 47         data.push_back(pinyin[shi]);
 48         data.push_back("Shi");
 49     }
 50     if (ge)
 51     {
 52         if (digit > 1)
 53             data.push_back(pinyin[0]);
 54         data.push_back(pinyin[ge]);
 55     }
 56 }
 57 
 58 int main()
 59 {
 60     int num;
 61     std::cin >> num;
 62     if (num == 0)
 63     {
 64         std::cout << pinyin[0];
 65         return 0;
 66     }
 67     if (num < 0)
 68     {
 69         num = -num;
 70         data.push_back("Fu");
 71     }
 72     int yi = num / 100000000;
 73     int wan = num % 100000000 / 10000;
 74     int ge = num % 10000;
 75     int seg = 0;
 76     if (yi)
 77     {
 78         seg = 2;
 79         data.push_back(pinyin[yi]);
 80         data.push_back("Yi");
 81     }
 82     if (wan)
 83     {
 84         if (wan < 1000 && seg > 1)
 85             data.push_back(pinyin[0]);
 86         seg = 1;
 87         chinese(wan);
 88         data.push_back("Wan");
 89     }
 90     if (ge)
 91     {
 92         if (ge < 1000 && seg > 0)
 93             data.push_back(pinyin[0]);
 94         chinese(ge);
 95     }
 96     int end = data.size() - 1;
 97     for (int i = 0; i != end; ++i)
 98         std::cout << data[i] << ' ';
 99     std::cout << data[end];
100 }

這是我第一次用拼音來命名變量。我也不想這樣,但誰讓這道題中文背景這麼明顯呢?

 

1095:

這道題要求模擬車輛進出校園的過程,這類模擬一個過程的題目,我稱之爲模擬類題。

模擬類題的一個通用方法就是以時間爲變量循環,可是這道題寫着寫着就用上另外一種方法了,就是以對象爲變量循環,這裏的對象就是車輛進出記錄。

題目邏輯比較複雜,大體能夠分爲3個步驟:

第一步,讀取全部記錄,並按時間順序排序,而後按記錄類型配對;

第二步,讀取查詢的時間,並模擬車輛進出的過程,這是模擬類題的核心(但在本題中佔的比例不大);

第三步,找出最長的停車時間,並輸出對應的車牌號(老司機開車!)。

梳理完這堆邏輯之後就直接上代碼吧:

 1 #include <iostream>
 2 #include <iomanip>
 3 #include <string>
 4 #include <vector>
 5 #include <map>
 6 #include <algorithm>
 7 
 8 int read_time()
 9 {
10     int res, temp;
11     std::cin >> temp;
12     res = temp * 3600;
13     std::cin.get();
14     std::cin >> temp;
15     res += temp * 60;
16     std::cin.get();
17     std::cin >> temp;
18     res += temp;
19     return res;
20 }
21 
22 void print_time(int time)
23 {
24     std::cout << std::setfill('0');
25     std::cout << std::setw(2) << time / 3600 << ':';
26     time %= 3600;
27     std::cout << std::setw(2) << time / 60 << ':';
28     std::cout << std::setw(2) << time % 60;
29 }
30 
31 enum class Status
32 {
33     in, out
34 };
35 
36 struct Record
37 {
38     std::string plate;
39     int time;
40     Status status;
41     bool paired = false;
42     int parking;
43 };
44 
45 int main()
46 {
47     int n, k;
48     std::cin >> n >> k;
49     std::vector<Record> records(n);
50     for (auto& r : records)
51     {
52         std::cin >> r.plate;
53         r.time = read_time();
54         std::string str;
55         std::cin >> str;
56         r.status = str == "in" ? Status::in : Status::out;
57     }
58     std::sort(records.begin(), records.end(), [](const Record& _lhs, const Record& _rhs) {
59         return _lhs.time < _rhs.time;
60     });
61     for (auto rec = records.begin(); rec != records.end(); ++rec)
62         if (rec->status == Status::in)
63             for (auto iter = rec + 1; iter != records.end(); ++iter)
64                 if (iter->plate == rec->plate && iter->status == Status::in)
65                     break;
66                 else if (iter->plate == rec->plate && iter->status == Status::out)
67                 {
68                     rec->paired = iter->paired = true;
69                     rec->parking = iter->time - rec->time;
70                     break;
71                 }
72 
73     auto iter = records.begin();
74     int count = 0;
75     for (int cnt = 0; cnt != k; ++cnt)
76     {
77         int time = read_time();
78         for (; iter != records.end() && iter->time <= time; ++iter)
79             if (iter->paired && iter->status == Status::in)
80                 ++count;
81             else if (iter->paired && iter->status == Status::out)
82                 --count;
83         std::cout << count << std::endl;
84     }
85 
86     std::map<std::string, int> parking;
87     for (const auto& rec : records)
88         if (rec.paired && rec.status == Status::in)
89             parking[rec.plate] += rec.parking;
90     int longest = 0;
91     for (const auto& car : parking)
92         if (car.second > longest)
93             longest = car.second;
94     for (const auto& car : parking)
95         if (car.second == longest)
96             std::cout << car.first << ' ';
97     print_time(longest);
98 }

這道題放在線性類,是由於配對和模擬的算法都是線性的(實際上是由於我看到它是模擬類就把它分給線性類了)。

 

總之,線性類題目難度不高,但坑很多,不只有各類邊界數據,還有卡時間常數的。

相關文章
相關標籤/搜索