競技プログラミングの過去問では、日付を扱う問題が頻出であり、特に省選レベルでは「時刻変換」「曖昧な日付の解釈」「回文日付の探索」の3つの典型的なパターンが見られます。これらは単なる文字列操作ではなく、日付の正当性検証・範囲制約・並び順保証といった実務的な要件を含むため、堅牢な実装が求められます。
ミリ秒から24時間表記への変換
入力として与えられたミリ秒単位の経過時間を、HH:MM:SS形式(24時間制、ゼロ埋め)に変換する問題です。1日は86,400,000ミリ秒(24×60×60×1000)であることに注意し、モジュロ演算で循環を処理します。
#include <iostream>
#include <cstdio>
using namespace std;
int main() {
long long millis;
cin >> millis;
long long total_seconds = millis / 1000;
long long hours = (total_seconds / 3600) % 24;
long long minutes = (total_seconds / 60) % 60;
long long seconds = total_seconds % 60;
printf("%02ld:%02ld:%02ld", hours, minutes, seconds);
return 0;
}
曖昧な日付表記の全候補列挙
「AA/BB/CC」形式の入力に対し、年号が2桁省略されているため、1960–2059年の範囲内で有効な日付候補をすべて生成し、昇順で出力します。3つの解釈パターン(YY/MM/DD、MM/DD/YY、DD/MM/YY)を網羅的に検証し、重複を排除してソート済み出力を得るには、単純な全探索が最も確実です。
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstdio>
using namespace std;
const int days_in_month[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
bool is_leap(int year) {
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}
bool is_valid_date(int y, int m, int d) {
if (y < 1960 || y > 2059 || m < 1 || m > 12) return false;
if (d < 1) return false;
if (m == 2 && is_leap(y)) {
return d <= 29;
}
return d <= days_in_month[m];
}
int full_year(int yy) {
return (yy >= 60) ? 1900 + yy : 2000 + yy;
}
int main() {
int a, b, c;
scanf("%d/%d/%d", &a, &b, &c);
vector<int> candidates;
// Pattern 1: YY/MM/DD → YYYY/MM/DD
int y1 = full_year(a);
if (is_valid_date(y1, b, c)) {
candidates.push_back(y1 * 10000 + b * 100 + c);
}
// Pattern 2: MM/DD/YY → YYYY/MM/DD
int y2 = full_year(c);
if (is_valid_date(y2, a, b)) {
candidates.push_back(y2 * 10000 + a * 100 + b);
}
// Pattern 3: DD/MM/YY → YYYY/MM/DD
if (a != b && is_valid_date(y2, b, a)) {
candidates.push_back(y2 * 10000 + b * 100 + a);
}
sort(candidates.begin(), candidates.end());
auto last = unique(candidates.begin(), candidates.end());
candidates.erase(last, candidates.end());
for (int date : candidates) {
int y = date / 10000;
int m = (date / 100) % 100;
int d = date % 100;
printf("%d-%02d-%02d\n", y, m, d);
}
return 0;
}
次回の回文日付探索
与えられた日付(整数形式:YYYYMMDD)より後の最初の「回文日付」と「ABAB型回文日付」(例:20200202 → A=2,B=0)をそれぞれ探す問題です。回文判定は文字列変換でも可能ですが、整数演算による桁分割が高速かつ安全です。閏年対応を含む日付妥当性チェックを必須とし、探索範囲は自然に制限されます(21世紀内なら最大20991231まで)。
#include <iostream>
#include <cstdio>
using namespace std;
const int mdays[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
bool is_leap(int y) {
return (y % 4 == 0 && y % 100 != 0) || (y % 400 == 0);
}
bool is_valid(int n) {
int y = n / 10000;
int m = (n / 100) % 100;
int d = n % 100;
if (m < 1 || m > 12) return false;
if (d < 1) return false;
if (m == 2 && is_leap(y)) return d <= 29;
return d <= mdays[m];
}
bool is_palindrome(int n) {
char s[9];
sprintf(s, "%08d", n);
for (int i = 0; i < 4; ++i) {
if (s[i] != s[7-i]) return false;
}
return true;
}
bool is_abab_palindrome(int n) {
char s[9];
sprintf(s, "%08d", n);
return s[0] == s[6] && s[1] == s[7] &&
s[0] == s[2] && s[1] == s[3] &&
s[0] != s[1];
}
int main() {
int start;
cin >> start;
bool found_first = false;
for (int candidate = start + 1; ; ++candidate) {
if (!is_valid(candidate)) continue;
if (!found_first && is_palindrome(candidate)) {
printf("%d\n", candidate);
found_first = true;
}
if (is_abab_palindrome(candidate)) {
printf("%d\n", candidate);
break;
}
}
return 0;
}