私自身が表題の問題を解いた時のプログラムについて解説します。
問題の詳細は「プレミアムデー」(CodeIQ)を参照してください。
プレミアムデー問題
その月の任意の曜日の最後の日付をプレミアムデーとします。
年月と曜日を表す数値を入力としてうけとり、プレミアムデーをYYYYMMDD形式で出力するプログラムを作成してください。
標準入力
例(2017年3月の金曜日)
- 年, 月, 曜日を表す数値がカンマ区切りで入力されます
- 年は4桁の数値です。入力される範囲は 2000-2100 です。
- 月は1-2桁の数値です。入力される範囲は 1-12 です。
例えば1月は1として入力されます。01として入力されることはありません。- 曜日を表す数値は以下の対応関係になっています。
0: 日曜日
1: 月曜日
2: 火曜日
3: 水曜日
4: 木曜日
5: 金曜日
6: 土曜日
2017,3,5
標準出力
- YYYYMMDD 形式で出力する
- MM 月は必ず2桁で0詰して表示する
1月: 01
10月: 10
例(入力の例に対する出力の例)
20170331
その他の仕様
・標準入力の末尾には改行があります
・標準出力の末尾に改行をつけてください
・存在しない日付の入力はありません
・標準入力の仕様で説明した内容以外の入力は行われません(不正入力に対するチェックは不要)
・暦はグレゴリオ暦を扱うものとします
Samples
Sample1
標準入力
2013,12,5
標準出力
20131227
Sample2
標準入力
2012,6,0
標準出力
20120624
Sample3
標準入力
2017,2,2
標準出力
20170228
Rubyで解答しています。
#!/usr/bin/ruby require 'date' def solve(y, m, wd) # 月ごとの日数 # 1 2 3 4 5 6 7 8 9 10 11 12 days = [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] d = days[m] # うるう年 d += (1 / (y % 4 + 1)) * (1 - 1 / (y % 100 + 1)) + (1 / (y % 400 + 1)) if m == 2 lwd = Date.new(y, m, d).wday x = (lwd >= wd) ? wd - lwd : wd - lwd - 7 return "%d%02d%02d"%[y,m,d+x] end # main while line = gets line.strip! if line.empty? then next end input = line.split(",").map{|a| a.to_i} puts solve(input[0], input[1], input[2]) end
この出題者の問題としては少し変わっていてやや難しい目です。
とはいえうるう年だけの問題で大したことはありません。
指定された年月の最後の日を求め、その曜日から指定された曜日を計算すれば良いだけです。
入力値を年,月,曜日に分けてsolve()に渡し、結果を印字します。
引数yは年、mは月、wdは曜日です。
yとmからその年のその月の最後の日を求めます。うるう年以外の月ごとの日数を定数daysにしておき、2月の場合だけうるう年かどうかを判断してうるう年なら1足します(12行目)。12行目の式は「うるう年 ワンライナー」とかで検索すれば見つかる式です。
指定された年月の最後の日がわかったら、最後の日からその曜日を求めます。何も考えずDateクラスを使って曜日を求めています。
15行目は最後の日から何日さかのぼれば指定の曜日になるかを計算しています。指定の曜日が月の最後の曜日以上の場合、単純に指定の曜日から最後の曜日を引けばさかのぼるべき日数がわかります(例:最後の曜日が3(水曜日)で指定が0(日曜日)なら-3日)。指定の曜日が月の最後の曜日未満の場合、指定の曜日から最後の曜日を引き、さらに-7するとさかのぼるべき日数になります(例:最後の曜日が3(水曜日)で指定が5(金曜日)なら-5日)。
後は書式をフォーマットして文字列として返却します。