このページについて
このページは、paiza ラーニング内に開設されているコンテンツ「レベルアップ問題集」で取り扱われているプログラミング課題について、独自の見解を述べたものです。
見解については、paizaラーニングの規約に基づき、許可されている範囲でのみ公開していますが、その内容については paiza とは一切関係なく、また paiza の立場を反映したものではありませんのでご注意ください。
挑戦する課題
レベルアップ問題集の日付セットから「曜日(large) (paizaランク B 相当) 」を取り上げます。
以下は、問題公開 Web ページからの引用です。
問題
西暦y年m月d日が何曜日か表示してください。
ただし、yとして非常に大きな値が与えられる可能性があることに注意してください。
ただし、各月の日数は以下のように決まることに注意してください。
- 4, 6, 9, 11月は30日
- 2月は閏年ならば29日、そうでなければ28日
- それ以外の月は31日
ただし、閏年は次のような年のことをいいます。
- 西暦が4で割り切れる年は閏年
- ただし、100で割り切れる年は平年
- ただし、400で割り切れる年は閏年
ただし、1800年1月1日は水曜日です。
入力される値
整数yとmとdが次のように、スペース区切りで1行で入力されます。
1 |
y m d |
期待する出力
以下のような形式で、答えを出力してください。
1 |
a曜日 |
- aは”月”, “火”, “水”, “木”, “金”, “土”, “日”のいずれかの文字です。
条件
すべてのテストケースにおいて、以下の条件をみたします。
- 1800≦y≦10^11
- 1≦m≦12
- 1≦d≦31
- y年m月d日は実際に存在する日付です。(2019年2月31日のような存在しない日付は与えられません)
考え方
この問題は、前問「曜日 (paizaランク B 相当)」をフェアフィールドの公式で解いたのであれば、それをそのまま使い回すことが可能な問題である。
この問題で気をつけなければならないのは、入力値の範囲である。
改めて条件を見ると
すべてのテストケースにおいて、以下の条件をみたします。
- 1800≦y≦10^11
という条件がある。 y が取りうる値の上限値が 10^11 乗となっているが、もし1800年からの経過日数を逐一計算して求めているようであれば、仮に計算結果が正しくとも時間切れとなってしまう。
設問にも「フェアフィールドの公式」へのリンクが準備されているので、それを利用しない方法はない。
解答例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
// 文字列を入力し、空白で区切り、Int 型にする let ia = readLine()!.split(separator: " ").map { Int($0)! } // 計算しやすいように、変数に入れ直す var y = ia[0] var m = ia[1] var d = ia[2] // 曜日番号 -> 曜日名に変換するための配列 var date = ["月", "火", "水", "木", "金", "土", "日"] // Fairfield の公式 func totalDate(_ y: Int, _ m: Int, _ d: Int) -> Int { return 365 * y + (y / 4) - ( y / 100) + (y / 400) + 306 * (m + 1) / 10 - 428 + d } // Fairfield の公式に適用させるためには、1年を 3月から14月に変形する必要があある。 if m == 1 || m == 2 { y -= 1 m += 12 } // 結果出力 print(date[(totalDate(y, m, d) - 1) % 7] + "曜日") |