PAT甲級題分類彙編——計算

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

 

計算類,指以數學運算爲主或爲背景的題。ios

題號 標題 分數 大意
1058 A+B in Hogwarts 20 特殊進制加法
1059 Prime Factors 25 分解素因數
1060 Are They Equal 25 必定精度下兩數是否相等
1065 A+B and C (64bit) 20 大數加法與比較
1069 The Black Hole of Numbers 20 黑洞數
1073 Scientific Notation 20 科學計數法還原
1081 Rational Sum 20 有理數加法
1088 Rational Arithmetic 20 有理數運算
1096 Consecutive Factors 20 最長連續因數
1100 Mars Numbers 20 進制轉換

這類題,廣泛分數低,大可能是20分。20分題放到乙級也沒啥問題,好比106九、1081等。然而,即便是20分題,也可能要卡好久。函數

我挑了1059106510881096共4道來作。spa

 

1059:code

題目要求輸出一個數的素因數分解。這麼簡單的題就很少講了吧,直接上代碼:htm

 1 #include <iostream>
 2 #include <map>
 3 #include <cmath>
 4 
 5 bool isprime(int n)
 6 {
 7     int end = std::sqrt(n) + 1;
 8     for (int i = 2; i != end; ++i)
 9         if (n % i == 0)
10             return false;
11     return true;
12 }
13 
14 int main(int argc, char const *argv[])
15 {
16     long n;
17     std::cin >> n;
18     std::cout << n << '=';
19     if (n == 1)
20     {
21         std::cout << '1';
22         return 0;
23     }
24     std::map<int, int> factors;
25     int prime = 2;
26     while (n > 1)
27     {
28         if (isprime(prime) && n % prime == 0)
29         {
30             ++factors[prime];
31             n /= prime;
32         }
33         else
34             ++prime;
35     }
36     int count = 0;
37     for (const auto& pair : factors)
38     {
39         if (count++)
40             std::cout << '*';
41         std::cout << pair.first;
42         if (pair.second > 1)
43             std::cout << '^' << pair.second;
44     }
45     return 0;
46 }

其實用 std::vector 也是能夠的,但我懶。blog

須要注意的是 n==1 的檢查,不過這個很容易發現。ci

 

1065:get

要求判斷兩個64位整數相加是否大於另外一個數。注意兩個64位整數相加之後若是還保存在64位整數中是可能溢出的,而且test case裏面必定會有這種狀況。數學

因此要用更大的整數來作計算。然而C++標準庫中並無提供128位整數。這時,偉大的g++出現了,它提供了 __int128_t :

 1 #include <iostream>
 2 
 3 int main(int argc, char const *argv[])
 4 {
 5     int t;
 6     std::cin >> t;
 7     std::cout << std::boolalpha;
 8 
 9     for (int i = 1; i <= t; ++i)
10     {
11         __int128_t a, b, c;
12         int64_t ta, tb, tc;
13         std::cin >> ta >> tb >> tc;
14         a = ta; b = tb; c = tc;
15         std::cout << "Case #" << i << ": " << (a + b > c) << std::endl;
16     }
17 
18     return 0;
19 }

因而這道題就被秒了。

C++標準庫對整數類型的支持不太好,大整數的類只能本身寫。至因而以二進制存儲仍是以十進制存儲,那要看應用場景了。

 

1088:

在一個case上卡了很久,最後發現題目裏有一句要用 long int ,WCNM。

難度沒什麼的,主要是對分子和分母有0或1的特殊狀況的處理。

優雅的我寫了一份很優雅的實現,又是template又是運算符重載的:

  1 #include <iostream>
  2 #include <exception>
  3 
  4 template <typename T>
  5 class Rational
  6 {
  7 public:
  8     Rational() = default;
  9     Rational(T _num, T _den)
 10         : numerator(_num), denominator(_den)
 11     {
 12         reduce();
 13     }
 14     Rational(const Rational&) = default;
 15     Rational& operator=(const Rational&) = default;
 16 
 17     Rational operator+(const Rational& _rhs) const noexcept
 18     {
 19         return Rational(numerator * _rhs.denominator + _rhs.numerator * denominator,
 20             denominator * _rhs.denominator);
 21     }
 22     Rational operator-(const Rational& _rhs) const noexcept
 23     {
 24         return Rational(numerator * _rhs.denominator - _rhs.numerator * denominator,
 25             denominator * _rhs.denominator);
 26     }
 27     Rational operator*(const Rational& _rhs) const noexcept
 28     {
 29         return Rational(numerator * _rhs.numerator,
 30             denominator * _rhs.denominator);
 31     }
 32     Rational operator/(const Rational& _rhs) const
 33     {
 34         if (_rhs.numerator == 0)
 35             throw std::exception();
 36         return Rational(numerator * _rhs.denominator,
 37             denominator * _rhs.numerator);
 38     }
 39 
 40     template <typename U>
 41     friend std::istream& operator>>(std::istream&, Rational<U>&);
 42     template <typename U>
 43     friend std::ostream& operator<<(std::ostream&, const Rational<U>&);
 44 private:
 45     static T gcd(T _lhs, T _rhs)
 46     {
 47         if (_lhs < 0)
 48             _lhs = -_lhs;
 49         if (_lhs < _rhs)
 50             return gcd(_rhs, _lhs);
 51         if (_rhs == 0)
 52             return _lhs ? _lhs : 1;
 53         return gcd(_rhs, _lhs % _rhs);
 54     }
 55 
 56     void reduce()
 57     {
 58         if (denominator < 0)
 59         {
 60             numerator = -numerator;
 61             denominator = -denominator;
 62         }
 63         auto factor = gcd(numerator, denominator);
 64         numerator /= factor;
 65         denominator /= factor;
 66     }
 67 
 68     T numerator;
 69     T denominator;
 70 };
 71 
 72 template <typename T>
 73 std::istream& operator>>(std::istream& _is, Rational<T>& _r)
 74 {
 75     T n, d;
 76     std::cin >> n;
 77     std::cin.get();
 78     std::cin >> d;
 79     _r = Rational<T>(n, d);
 80     return _is;
 81 }
 82 
 83 template <typename T>
 84 std::ostream& operator<<(std::ostream& _os, const Rational<T>& _r)
 85 {
 86     auto r = _r;
 87     bool neg = false;
 88     if (r.numerator < 0)
 89     {
 90         neg = true;
 91         r.numerator = -r.numerator;
 92         _os << "(-";
 93     }
 94     if (r.denominator == 1)
 95         _os << r.numerator;
 96     else
 97     {
 98         if (r.numerator >= r.denominator)
 99         {
100             _os << (r.numerator / r.denominator) << ' ';
101             r.numerator %= r.denominator;
102         }
103         _os << r.numerator << '/' << r.denominator;
104     }
105     if (neg)
106         _os << ')';
107     return _os;
108 }
109 
110 int main(int argc, char const *argv[])
111 {
112     Rational<long long> lhs, rhs;
113     std::cin >> lhs >> rhs;
114     std::cout << lhs << " + " << rhs << " = " << (lhs + rhs) << std::endl;
115     std::cout << lhs << " - " << rhs << " = " << (lhs - rhs) << std::endl;
116     std::cout << lhs << " * " << rhs << " = " << (lhs * rhs) << std::endl;
117     std::cout << lhs << " / " << rhs << " = ";
118     try
119     {
120         std::cout << (lhs / rhs) << std::endl;
121     }
122     catch(const std::exception& e)
123     {
124         std::cout << "Inf" << std::endl;
125     }
126     return 0;
127 }

優雅個屁,考試要來不及的。

順便講講寫自定義類(特指有成員方法的類而非簡單的結構體)和模板的技巧。

構造函數最好給一個默認的,Rule of Three/Five規定的方法最好顯式 =default 寫出來。

參數儘可能用引用,看狀況要不要加 const ,大多都是要加的。相應地,成員方法能用 const 修飾的必定要加上去。

對於模板類的友元函數,模板類中還要再套模板,並且要用不同的類型名稱。

以上規則若是不遵照,極可能會編譯錯誤,並且錯誤信息看起來比較累。

 

1096:

要求輸出給定整數的的分解中能夠出現的最長連續因數。乍一看有一點難,但稍微想想就會發現,這串因數不會太長,由於階乘是很大的。因此能夠一個一個試這串因數中的最小數,連續不少個因數只有沒幾種狀況須要試,到連續兩個的狀況,也是O(sqrt(n))時間複雜度能搞定的。至於單個因數則須要單獨討論一下,由於試到根號n就能夠了,而若是不中止的話會超時。

 1 #include <iostream>
 2 #include <utility>
 3 #include <cmath>
 4 
 5 int main(int argc, char const *argv[])
 6 {
 7     int n;
 8     std::cin >> n;
 9 
10     try
11     {
12         for (int count = 11; count > 1; --count)
13         {
14             for (int min = 2; ; ++min)
15             {
16                 long product = 1;
17                 for (int factor = min; factor != min + count; ++factor)
18                     product *= factor;
19                 if (product > n)
20                     break;
21                 if (n % product == 0)
22                     throw std::pair<int, int>(count, min);
23             }
24         }
25         int end = std::sqrt(n) + 1;
26         for (int factor = 2; factor != end; ++factor)
27             if (n % factor == 0)
28                 throw std::pair<int, int>(1, factor);
29         throw std::pair<int, int>(1, n);
30     }
31     catch(const std::pair<int, int>& pair)
32     {
33         std::cout << pair.first << std::endl;
34         for (int i = pair.second; i != pair.second + pair.first - 1; ++i)
35             std::cout << i << '*';
36         std::cout << pair.second + pair.first - 1 << std::endl;
37     }
38 
39     return 0;
40 }

長度的初始值是11,是由於2*3*...*13是大於2^31的,而2*3*...*12是小於2^31的,所以長度不會超過11。

 

總之,計算類題目沒有難度,可是要避坑。坑依然是邊界條件,包括最小的數(0、一、2等)與很大的數(int放不下的)。

相關文章
相關標籤/搜索