AtCoder Beginner Contest(ABC)のA問題を、Common Lispで順番に解いていきます。
段階ごとに新しい機能を少しずつ覚えながら、50問を解いていきましょう。
1. 関数定義と基本の入出力
defun で関数を定義します。
計算は関数に、入出力は main に書く形で進めます。
(defun 関数名 (引数1 引数2)
処理)Code language: Lisp (lisp)
readで数値を1つ読み込みます。
スペースや改行で区切られていても、呼ぶたびに順番に読んでくれます。read-lineは1行をまるごと文字列として読みます。princで値を出力します。#\Spaceは空白文字、#\Newlineは改行文字です。
あるいは、terpriで改行することもできます。
(princ 値)
(princ #\Space)
(terpri)Code language: Lisp (lisp)
let* は変数をまとめて作る書き方で、上から順番に評価されます。
前の変数を次の定義で使えます。
(let* ((変数名 式)...)
式... )Code language: Lisp (lisp)
let は同時に評価されるため、前の変数を参照できません。
定義した変数を後の変数定義に使える let* が便利です。
1.1. 問題 0 — PracticeA: Welcome to AtCoder
整数 a,b,cと、文字列 s が与えられます。
a+b+c の計算結果と、文字列 s を並べて表示しなさい。
1.2. 解答
(defun sum-three (a b c)
(+ a b c))
(defun main ()
(let* ((a (read))
(b (read))
(c (read))
(s (read-line)))
(princ (sum-three a b c))
(princ #\Space)
(princ s)))
(main)Code language: Lisp (lisp)
let* は変数をまとめて作る書き方で、上から順番に評価されます。
a、b、c を数値として読んだあと、read-line で残りの行を文字列として読んでいます。
read は数値を読み終えたあとの空白も一緒に消費するので、次の read-line はちょうど次の行から読み始めます。
この問題は数値と文字列を同じ行に出力するため、#\Space で空白を挟んでいます。
2. 第1段階:計算だけ
数値を読んで、計算して、出力する。
この3ステップだけで解ける問題です。
四則演算
(+ a b) ; 足し算
(- a b) ; 引き算
(* a b) ; 掛け算
(floor a b) ; 切り捨て除算。
(floor 7 2) => 3Code language: Lisp (lisp)
大きい方・小さい方を選ぶ
(max a b c) ; 最大値
(min a b c) ; 最小値Code language: Lisp (lisp)
2.1. 問題 1 — ABC116A: Right Triangle
直角三角形 ABC があり、角 B が直角です。
辺 AB、BC、CA の長さ a b c が与えられます。
直角をはさむ辺は AB と BC なので、三角形の面積を出力してください。
解答
(defun area (a b)
(floor (* a b) 2))
(defun main ()
(let ((a (read))
(b (read)))
(read)
(princ (area a b))))
(main)Code language: Lisp (lisp)
a * b / 2 を floor で整数として計算しています。
3つ目の辺 c は使わないので read で読み捨てています。
2.2. 問題 2 — ABC113A: Discount Fare
整数 X と Y が与えられます。X + Y/2 を計算して出力してください。
解答
(defun fare (x y)
(+ x (floor y 2)))
(defun main ()
(let ((x (read))
(y (read)))
(princ (fare x y))))
(main)Code language: Lisp (lisp)
Y/2 は floor で切り捨て除算しています。
2.3. 問題 3 — ABC069A: K-City
縦 N、横 M のマス目があります。
外側の1周を除いた内側のマス数を出力してください。
解答
(defun inner-cells (n m)
(* (- n 1) (- m 1)))
(defun main ()
(let ((n (read))
(m (read)))
(princ (inner-cells n m))))
(main)Code language: Lisp (lisp)
縦横それぞれ端の1行・1列を除いた数を掛けています。
2.4. 問題 4 — ABC098A: Addition and Subtraction Easy
整数 A と B が与えられます。A+B、A-B、A*B の3つの値のうち、最大値を出力してください。
解答
(defun best-value (a b)
(max (+ a b) (- a b) (* a b)))
(defun main ()
(let ((a (read))
(b (read)))
(princ (best-value a b))))
(main)Code language: Lisp (lisp)
max に3つの値を渡してまとめて比較しています。
2.5. 問題 5 — ABC137A: +-x
整数 A と B が与えられます。A+B、A-B、A*B の3つの値のうち、最大値を出力してください。
解答
(defun best-value (a b)
(max (+ a b) (- a b) (* a b)))
(defun main ()
(let ((a (read))
(b (read)))
(princ (best-value a b))))
(main)Code language: Lisp (lisp)
問題4と同じ解法です。
同じ発想がそのまま使えます。
2.6. 問題 6 — ABC107A: Train
N 両編成の電車があります。
前から i 両目の車両は、後ろから数えると何両目かを出力してください。
解答
(defun from-back (n i)
(+ (- n i) 1))
(defun main ()
(let ((n (read))
(i (read)))
(princ (from-back n i))))
(main)Code language: Lisp (lisp)
前から i 両目は後ろから N - i + 1 両目です。
2.7. 問題 7 — ABC140A: Password
各桁に 1 から N までの数字を使える3桁のパスワードを作ります。
作れるパスワードの総数を出力してください。
解答
(defun password-count (n)
(* n n n))
(defun main ()
(let ((n (read)))
(princ (password-count n))))
(main)Code language: Lisp (lisp)
各桁が独立して N 通りあるので N の3乗が答えです。
2.8. 問題 8 — ABC121A: White Cells
縦 H、横 W の白いマス目があります。
そのうち h 行と w 列を黒く塗ります。
白く残るマス数を出力してください。
解答
(defun white-cells (h w hh ww)
(* (- h hh) (- w ww)))
(defun main ()
(let ((h (read))
(w (read))
(hh (read))
(ww (read)))
(princ (white-cells h w hh ww))))
(main)Code language: Lisp (lisp)
黒く塗らない行数と列数をそれぞれ掛けています。
2.9. 問題 9 — ABC129A: Airplane
空港間の移動時間 P Q R が与えられます。
3つのうち2つの移動を選ぶとき、合計時間の最小値を出力してください。
解答
(defun shortest-two (p q r)
(min (+ p q) (+ q r) (+ r p)))
(defun main ()
(let ((p (read))
(q (read))
(r (read)))
(princ (shortest-two p q r))))
(main)Code language: Lisp (lisp)
3通りの組み合わせを min でまとめて比較しています。
3. 第2段階:if による条件分岐
条件によって出力を変える問題です。
条件分岐
(if 条件 真のとき 偽のとき)Code language: Lisp (lisp)
比較演算子
(= a b) ; 等しい
(< a b) ; a が b より小さい
(<= a b) ; a が b 以下
(> a b) ; a が b より大きい
(>= a b) ; a が b 以上
(<= a b c) ; a 以上 c 以下(連鎖比較)Code language: Lisp (lisp)
複数条件をまとめる
(and 条件1 条件2) ; 両方が真のとき真
(or 条件1 条件2) ; どちらかが真のとき真Code language: Lisp (lisp)
余りの計算
(mod a b) ; a を b で割った余り
(evenp n) ; n が偶数なら T
(oddp n) ; n が奇数なら T
(zerop n) ; n が 0 なら TCode language: Lisp (lisp)
3.1. 問題 10 — ABC099A: ABD
整数 N が与えられます。N が999以下なら ABC、1000以上なら ABD を出力してください。
解答
(defun contest-name (n)
(if (<= n 999) "ABC" "ABD"))
(defun main ()
(let ((n (read)))
(princ (contest-name n))))
(main)Code language: Lisp (lisp)
境界値 999 で if を使って分岐しています。
3.2. 問題 11 — ABC130A: Rounding
整数 X と A が与えられます。X が A 未満なら 0、そうでなければ 10 を出力してください。
解答
(defun score (x a)
(if (< x a) 0 10))
(defun main ()
(let ((x (read))
(a (read)))
(princ (score x a))))
(main)Code language: Lisp (lisp)
if で境界を判定して数値を返しています。
3.3. 問題 12 — ABC083A: Libra
4つの整数 A B C D が与えられます。A+B と C+D を比較し、前者が大きければ Left、後者が大きければ Right、等しければ Balanced を出力してください。
解答
(defun compare-sums (a b c d)
(let ((left (+ a b))
(right (+ c d)))
(cond ((> left right) "Left")
((< left right) "Right")
(t "Balanced"))))
(defun main ()
(let ((a (read))
(b (read))
(c (read))
(d (read)))
(princ (compare-sums a b c d))))
(main)Code language: Lisp (lisp)
3通りに分岐するので cond を使っています。cond は (cond (条件1 値1) (条件2 値2) (t デフォルト)) の形で書きます。
3.4. 問題 13 — ABC100A: Happy Birthday!
整数 A と B が与えられます。
どちらも8以下なら Yay!、そうでなければ :( を出力してください。
解答
(defun happy-p (a b)
(if (and (<= a 8) (<= b 8)) "Yay!" ":("))
(defun main ()
(let ((a (read))
(b (read)))
(princ (happy-p a b))))
(main)Code language: Lisp (lisp)
and で2つの条件を同時に確認しています。
3.5. 問題 14 — ABC094A: Cats and Dogs
犬が A 匹、猫が B 匹います。
合計で X 匹になるように選べるかを判定してください。
可能なら YES、不可能なら NO を出力します。
解答
(defun possible-p (a b x)
(if (<= a x (+ a b)) "YES" "NO"))
(defun main ()
(let ((a (read))
(b (read))
(x (read)))
(princ (possible-p a b x))))
(main)Code language: Lisp (lisp)
(<= a x (+ a b)) は「a 以上かつ a+b 以下」という意味の連鎖比較です。
3.6. 問題 15 — ABC118A: B +/- A
整数 A と B が与えられます。B が A の倍数なら A+B、そうでなければ B-A を出力してください。
解答
(defun value (a b)
(if (zerop (mod b a))
(+ a b)
(- b a)))
(defun main ()
(let ((a (read))
(b (read)))
(princ (value a b))))
(main)Code language: Lisp (lisp)
mod の結果が 0 かどうかで倍数を判定しています。
3.7. 問題 16 — ABC109A: ABC333
整数 A と B が与えられます。A*B が奇数なら Yes、偶数なら No を出力してください。
解答
(defun odd-product-p (a b)
(if (oddp (* a b)) "Yes" "No"))
(defun main ()
(let ((a (read))
(b (read)))
(princ (odd-product-p a b))))
(main)Code language: Lisp (lisp)
oddp で積の奇偶を判定しています。
3.8. 問題 17 — ABC144A: 9×9
整数 A と B が与えられます。
どちらも1以上9以下なら A*B を出力し、そうでなければ -1 を出力してください。
解答
(defun product-or-minus-one (a b)
(if (and (<= 1 a 9) (<= 1 b 9))
(* a b)
-1))
(defun main ()
(let ((a (read))
(b (read)))
(princ (product-or-minus-one a b))))
(main)Code language: Lisp (lisp)
(<= 1 a 9) で「1以上9以下」を一度に判定しています。
4. 第3段階:複数分岐
3つ以上の場合分けが必要な問題です。
複数分岐には cond を使います。
(cond (条件1 値1)
(条件2 値2)
(t デフォルト値))Code language: Lisp (lisp)
上から順に条件を確認して、最初に真になったものの値を返します。t は常に真なので、最後のデフォルトとして使います。
文字列の比較
(string= s1 s2) ; 文字列が等しいか
(string<= s1 s2) ; 辞書順で s1 が s2 以前かCode language: Lisp (lisp)
4.1. 問題 18 — ABC104A: Rated for Me
整数 R が与えられます。R が1200未満なら ABC、1200以上2800未満なら ARC、2800以上なら AGC を出力してください。
解答
(defun contest-for-rate (r)
(cond ((< r 1200) "ABC")
((< r 2800) "ARC")
(t "AGC")))
(defun main ()
(let ((r (read)))
(princ (contest-for-rate r))))
(main)Code language: Lisp (lisp)
3段階の境界を cond で上から確認しています。
4.2. 問題 19 — ABC115A: Christmas Eve Eve Eve
整数 D(22〜25のいずれか)が与えられます。
25なら Christmas、24なら Christmas Eve、23なら Christmas Eve Eve、22なら Christmas Eve Eve Eve を出力してください。
解答
(defun christmas-name (d)
(cond ((= d 25) "Christmas")
((= d 24) "Christmas Eve")
((= d 23) "Christmas Eve Eve")
(t "Christmas Eve Eve Eve")))
(defun main ()
(let ((d (read)))
(princ (christmas-name d))))
(main)Code language: Lisp (lisp)
日付と文字列の対応を cond で並べています。
4.3. 問題 20 — ABC141A: Weather Prediction
今日の天気 S が与えられます。Sunny の次は Cloudy、Cloudy の次は Rainy、Rainy の次は Sunny です。
明日の天気を出力してください。
解答
(defun next-weather (s)
(cond ((string= s "Sunny") "Cloudy")
((string= s "Cloudy") "Rainy")
(t "Sunny")))
(defun main ()
(let ((s (read-line)))
(princ (next-weather s))))
(main)Code language: Lisp (lisp)
文字列の比較には string= を使います。read-line で1行まとめて読んでいます。
4.4. 問題 21 — ABC146A: Can’t Wait for Holiday
曜日 S が英語3文字(SUN 〜 SAT)で与えられます。
次の日曜日まで何日かを出力してください。
日曜なら7を出力します。
解答
(defun days-to-sunday (s)
(cond ((string= s "SUN") 7)
((string= s "MON") 6)
((string= s "TUE") 5)
((string= s "WED") 4)
((string= s "THU") 3)
((string= s "FRI") 2)
(t 1)))
(defun main ()
(let ((s (read-line)))
(princ (days-to-sunday s))))
(main)Code language: Lisp (lisp)
各曜日と日数を cond で対応させています。
4.5. 問題 22 — ABC122A: Double Helix
文字 b(A、T、C、G のいずれか)が1文字与えられます。
相補的な文字(A↔T、C↔G)を出力してください。
解答
(defun complement-base (c)
(cond ((char= c #\A) #\T)
((char= c #\T) #\A)
((char= c #\C) #\G)
(t #\C)))
(defun main ()
(let ((s (read-line)))
(princ (complement-base (char s 0)))))
(main)Code language: Lisp (lisp)
char で文字列の先頭文字を取り出して cond で対応する文字を返しています。
4.6. 問題 23 — ABC127A: Ferris Wheel
年齢 A と通常料金 B が与えられます。
6以下なら無料、7〜12なら半額、13以上なら通常料金です。
支払う料金を出力してください。
解答
(defun fee (a b)
(cond ((<= a 6) 0)
((<= a 12) (floor b 2))
(t b)))
(defun main ()
(let ((a (read))
(b (read)))
(princ (fee a b))))
(main)Code language: Lisp (lisp)
半額は floor で切り捨て除算しています。
4.7. 問題 24 — ABC156A: Beginner
整数 N と R が与えられます。N が10以上なら R を出力します。N が10未満なら R + 100 * (10 - N) を出力してください。
解答
(defun adjusted-rating (n r)
(if (>= n 10)
r
(+ r (* 100 (- 10 n)))))
(defun main ()
(let ((n (read))
(r (read)))
(princ (adjusted-rating n r))))
(main)Code language: Lisp (lisp)
10回未満のぶん、足りない回数 × 100 点を加算しています。
5. 第4段階:文字列操作
文字列を読んで、文字を数えたり変換したりする問題です。
文字列から文字を取り出す
(char s i) ; 文字列 s の i 番目の文字(0始まり)
(char= c1 c2) ; 文字を比較するCode language: Lisp (lisp)
文字を数える
(count #\o s) ; 文字列 s 中の文字 o の個数Code language: Lisp (lisp)
文字コードの変換
(char-code c) ; 文字 c の文字コード(整数)
(code-char n) ; 文字コード n に対応する文字
(char-downcase c) ; 文字を小文字にするCode language: Lisp (lisp)
文字列を変換する
(coerce (loop for c across s collect ...) 'string)
; 文字列の各文字を変換して新しい文字列を作る
(copy-seq s) ; 文字列をコピーするCode language: Lisp (lisp)
重複を取り除く
(remove-duplicates リスト) ; リストの重複を取り除くCode language: Lisp (lisp)
5.1. 問題 25 — ABC095A: Something on It
3文字の文字列 S が与えられます。
基本価格は700円です。S に含まれる o の数だけ100円を足した金額を出力してください。
解答
(defun pizza-price (s)
(+ 700 (* 100 (count #\o s))))
(defun main ()
(let ((s (read-line)))
(princ (pizza-price s))))
(main)Code language: Lisp (lisp)
o の個数を count で数えて、100円ずつ加算しています。
5.2. 問題 26 — ABC111A: AtCoder Beginner Contest
1 と 9 だけからなる3文字の文字列 N が与えられます。1 を 9 に、9 を 1 に置き換えた文字列を出力してください。
解答
(defun swap-1-9-char (c)
(if (char= c #\1) #\9 #\1))
(defun swap-1-9 (s)
(coerce (loop for c across s collect (swap-1-9-char c))
'string))
(defun main ()
(let ((s (read-line)))
(princ (swap-1-9 s))))
(main)Code language: Lisp (lisp)
loop for c across s で文字列を1文字ずつ取り出し、変換した文字を collect でまとめて coerce で文字列に戻しています。
5.3. 問題 27 — ABC126A: Changing a Character
長さ N の文字列 S と整数 K が与えられます。S の K 文字目だけを小文字にして出力してください。
解答
(defun change-char (s k)
(let ((result (copy-seq s)))
(setf (char result (- k 1))
(char-downcase (char result (- k 1))))
result))
(defun main ()
(let ((n (read))
(k (read)))
(declare (ignore n))
(read-line)
(let ((s (read-line)))
(princ (change-char s k)))))
(main)Code language: Lisp (lisp)
copy-seq で文字列をコピーしてから、K-1番目(0始まり)の文字を char-downcase で小文字に変えています。
5.4. 問題 28 — ABC151A: Next Alphabet
英小文字 c が1文字与えられます。
アルファベット順で次の文字を出力してください。
解答
(defun next-char (c)
(code-char (+ (char-code c) 1)))
(defun main ()
(let ((s (read-line)))
(princ (next-char (char s 0)))))
(main)Code language: Lisp (lisp)
char-code で文字コードを取得して +1 し、code-char で文字に戻しています。
5.5. 問題 29 — ABC158A: Station and Bus
A と B だけからなる3文字の文字列 S が与えられます。
3文字がすべて同じなら No、そうでなければ Yes を出力してください。
解答
(defun mixed-p (s)
(if (and (char= (char s 0) (char s 1))
(char= (char s 1) (char s 2)))
"No"
"Yes"))
(defun main ()
(let ((s (read-line)))
(princ (mixed-p s))))
(main)Code language: Lisp (lisp)
隣り合う文字をすべて比較して、全部一致なら No としています。
5.6. 問題 30 — ABC132A: Fifty-Fifty
4文字の文字列 S が与えられます。
2種類の文字がそれぞれ2回ずつ現れているなら Yes、そうでなければ No を出力してください。
解答
(defun fifty-fifty-p (s)
(let ((chars (remove-duplicates (coerce s 'list))))
(if (and (= (length chars) 2)
(loop for c in chars always (= (count c s) 2)))
"Yes"
"No")))
(defun main ()
(let ((s (read-line)))
(princ (fifty-fifty-p s))))
(main)Code language: Lisp (lisp)
coerce でリストに変換してから remove-duplicates で重複を除き、種類数と各文字の個数を確認しています。
5.7. 問題 31 — ABC119A: Still TBD
日付文字列 S が YYYY/MM/DD の形式で与えられます。2019/04/30 以前なら Heisei、それより後なら TBD を出力してください。
解答
(defun era (s)
(if (string<= s "2019/04/30") "Heisei" "TBD"))
(defun main ()
(let ((s (read-line)))
(princ (era s))))
(main)Code language: Lisp (lisp)
日付文字列は辞書順と日付の順序が一致するので、string<= でそのまま比べられます。
6. 第5段階:計算の応用
floor、ceiling、min、max を組み合わせて解く問題です。
6.1. この段階で使う機能
切り上げ除算
(ceiling a b) ; a を b で割って切り上げ。
(ceiling 7 2) => 4Code language: Lisp (lisp)
リストの集計
(apply #'min リスト) ; リストの最小値
(apply #'max リスト) ; リストの最大値
(apply #'+ リスト) ; リストの合計Code language: Lisp (lisp)
リストの操作
(list a b c) ; 値をリストにまとめる
(sort リスト #'<) ; 昇順に並べ替える
(first リスト) ; 最初の要素
(third リスト) ; 3番目の要素
(remove-duplicates リスト) ; 重複を取り除く
(length リスト) ; 要素数Code language: Lisp (lisp)
6.2. 問題 32 — ABC089A: Grouping 2
整数 N が与えられます。N 人を3人組にするとき、最大で何組作れるかを出力してください。
解答
(defun groups (n)
(floor n 3))
(defun main ()
(let ((n (read)))
(princ (groups n))))
(main)Code language: Lisp (lisp)
3で割った商が組数です。
6.3. 問題 33 — ABC102A: Multiple of 2 and N
整数 N が与えられます。N の倍数で、かつ2の倍数でもある最小の正整数を出力してください。
解答
(defun least-multiple (n)
(if (evenp n) n (* 2 n)))
(defun main ()
(let ((n (read)))
(princ (least-multiple n))))
(main)Code language: Lisp (lisp)
N が偶数ならそのまま N、奇数なら 2N が答えです。
6.4. 問題 34 — ABC108A: Pair
1以上 K 以下の整数から2つ選びます。
片方が奇数、片方が偶数になるような組の数を出力してください。
解答
(defun pair-count (k)
(let ((evens (floor k 2))
(odds (- k (floor k 2))))
(* evens odds)))
(defun main ()
(let ((k (read)))
(princ (pair-count k))))
(main)Code language: Lisp (lisp)
偶数の個数と奇数の個数を求めて掛けています。
6.5. 問題 35 — ABC105A: AtCoder Crackers
N 個のクラッカーを K 人で分けます。
全員に同じ個数ずつ配りきれるなら 0、そうでなければ 1 を出力してください。
解答
(defun need-extra-p (n k)
(if (zerop (mod n k)) 0 1))
(defun main ()
(let ((n (read))
(k (read)))
(princ (need-extra-p n k))))
(main)Code language: Lisp (lisp)
N を K で割った余りが 0 かどうかで判定しています。
6.6. 問題 36 — ABC128A: Apple Pie
りんごが A 個、りんごのかけらが P 個あります。
りんご1個は3かけらにできます。
かけら2個でアップルパイを1個作るとき、作れる最大数を出力してください。
解答
(defun pie-count (a p)
(floor (+ (* 3 a) p) 2))
(defun main ()
(let ((a (read))
(p (read)))
(princ (pie-count a p))))
(main)Code language: Lisp (lisp)
りんごをすべてかけらに換算してから 2 で割っています。
6.7. 問題 37 — ABC087A: Buying Sweets
所持金 X 円から A 円の商品を買い、残りで B 円のお菓子をできるだけ買います。
最後に余る金額を出力してください。
解答
(defun remainder (x a b)
(mod (- x a) b))
(defun main ()
(let ((x (read))
(a (read))
(b (read)))
(princ (remainder x a b))))
(main)Code language: Lisp (lisp)
A 円を引いた残りを B で割った余りが答えです。
6.8. 問題 38 — ABC120A: Favorite Sound
1個 A 円の品物があります。
所持金は B 円で、最大でも C 個までしか買いません。
買える個数を出力してください。
解答
(defun buy-count (a b c)
(min (floor b a) c))
(defun main ()
(let ((a (read))
(b (read))
(c (read)))
(princ (buy-count a b c))))
(main)Code language: Lisp (lisp)
お金で買える個数と上限 C の小さい方が答えです。
6.9. 問題 39 — ABC136A: Transfer
容量 A の容器に B 入っています。
さらに C 入れようとします。
入りきらずに余る量を出力してください。
解答
(defun overflow (a b c)
(max 0 (- c (- a b))))
(defun main ()
(let ((a (read))
(b (read))
(c (read)))
(princ (overflow a b c))))
(main)Code language: Lisp (lisp)
空き容量 A-B を超えた分が余りです。
マイナスにならないよう max 0 で切っています。
6.10. 問題 40 — ABC092A: Traveling Budget
行きの交通費候補 A 円と B 円、帰りの交通費候補 C 円と D 円が与えられます。
それぞれ安い方を選んだ合計金額を出力してください。
解答
(defun min-cost (a b c d)
(+ (min a b) (min c d)))
(defun main ()
(let ((a (read))
(b (read))
(c (read))
(d (read)))
(princ (min-cost a b c d))))
(main)Code language: Lisp (lisp)
行きの最小と帰りの最小をそれぞれ求めて足しています。
6.11. 問題 41 — ABC124A: Buttons
2つのボタンがあり、押すと A 点、B 点が得られます。
同じボタンを押すたびに得点は1減ります。
2回押したときの最大得点を出力してください。
解答
(defun max-score (a b)
(if (> a b)
(+ a (max (- a 1) b))
(+ b (max a (- b 1)))))
(defun main ()
(let ((a (read))
(b (read)))
(princ (max-score a b))))
(main)Code language: Lisp (lisp)
得点の高いボタンを先に押し、2回目は同じボタンを再度押すか別のボタンを押すかで得点が変わります。
6.12. 問題 42 — ABC153A: Serval vs Monster
体力 H のモンスターに、1回 A ダメージを与えます。
倒すのに必要な最小攻撃回数を出力してください。
解答
(defun attacks (h a)
(ceiling h a))
(defun main ()
(let ((h (read))
(a (read)))
(princ (attacks h a))))
(main)Code language: Lisp (lisp)
ceiling で切り上げ除算しています。
6.13. 問題 43 — ABC133A: T or T
N 人で移動します。
電車は1人 A 円、タクシーは全員で B 円です。
安い方の料金を出力してください。
解答
(defun cheaper (n a b)
(min (* n a) b))
(defun main ()
(let ((n (read))
(a (read))
(b (read)))
(princ (cheaper n a b))))
(main)Code language: Lisp (lisp)
電車の合計 N*A とタクシー料金 B の小さい方を選んでいます。
6.14. 問題 44 — ABC157A: Duplex Printing
N ページの資料を両面印刷します。
必要な紙の枚数を出力してください。
解答
(defun sheets (n)
(ceiling n 2))
(defun main ()
(let ((n (read)))
(princ (sheets n))))
(main)Code language: Lisp (lisp)
2ページで1枚なので ceiling で切り上げています。
6.15. 問題 45 — ABC155A: Poor
3つの整数 A B C が与えられます。
ちょうど2つだけが同じ値なら Yes、そうでなければ No を出力してください。
解答
(defun poor-p (a b c)
(let ((k (length (remove-duplicates (list a b c)))))
(if (= k 2) "Yes" "No")))
(defun main ()
(let ((a (read))
(b (read))
(c (read)))
(princ (poor-p a b c))))
(main)Code language: Lisp (lisp)
remove-duplicates で重複を除いた後の要素数が 2 なら「ちょうど2種類」です。
6.16. 問題 46 — ABC148A: Round One
1、2、3のうち異なる2つの整数 A と B が与えられます。
与えられていない残りの整数を出力してください。
解答
(defun remaining-number (a b)
(- 6 a b))
(defun main ()
(let ((a (read))
(b (read)))
(princ (remaining-number a b))))
(main)Code language: Lisp (lisp)
1+2+3=6 なので、6から2つを引けば残りが出ます。
6.17. 問題 47 — ABC154A: Remaining Balls
2種類のボール名 S と T、それぞれの個数 A と B、取り出したボール名 U が与えられます。U と同じ名前のボールを1個減らし、残りの個数を出力してください。
解答
(defun main ()
(let* ((s (read))
(tt (read))
(a (read))
(b (read))
(u (read)))
(if (eq s u)
(decf a)
(decf b))
(princ a)
(princ #\Space)
(princ b)))
(main)Code language: Lisp (lisp)
t はCommon Lispの予約語なので変数名を tt にしています。decf は変数を1減らします。
6.18. 問題 48 — ABC112A: Programming Education
整数 N が与えられます。N が1なら Hello World を出力します。N が2なら、続けて整数 A と B が与えられるので、その和を出力してください。
解答
(defun main ()
(let ((n (read)))
(if (= n 1)
(princ "Hello World")
(let ((a (read))
(b (read)))
(princ (+ a b))))))
(main)Code language: Lisp (lisp)
N=2 のときだけ追加で2つ読んで足しています。
6.19. 問題 49 — ABC096A: Day of Takahashi
整数 A と B が与えられます。
1月1日、2月2日…のように月と日が同じ日を、A 月 B 日までに何回迎えるかを出力してください。
解答
(defun count-days (a b)
(if (>= b a) a (- a 1)))
(defun main ()
(let ((a (read))
(b (read)))
(princ (count-days a b))))
(main)Code language: Lisp (lisp)
B 日が A 以上なら A 月A日まで含めて A 回、未満なら A-1 回です。
6.20. 問題 50 — ABC103A: Task Scheduling Problem
3つの整数が与えられます。
3つの仕事を好きな順に行うとき、隣り合う仕事の難しさの差の絶対値の合計の最小値を出力してください。
解答
(defun min-diff-sum (a b c)
(let ((lst (sort (list a b c) #'<)))
(- (third lst) (first lst))))
(defun main ()
(let ((a (read))
(b (read))
(c (read)))
(princ (min-diff-sum a b c))))
(main)Code language: Lisp (lisp)
昇順に並べると最大値と最小値の差が最小コストになります。
7. 登場した機能まとめ
| 機能 | 説明 |
|---|---|
read | 数値やシンボルを1つ読む |
read-line | 1行を文字列として読む |
princ | 値を出力する |
#\Space | 空白文字 |
terpri | 改行する |
let / let* | 変数を作る |
declare (ignore ...) | 使わない変数を無視する宣言 |
if | 2通りに分岐する |
cond | 複数通りに分岐する |
and / or | 条件を組み合わせる |
= < <= > >= | 数値の比較 |
(<= a b c) | 連鎖比較(a以上c以下) |
string= string<= | 文字列の比較 |
char= | 文字の比較 |
+ - * | 四則演算 |
floor | 切り捨て除算 |
ceiling | 切り上げ除算 |
mod | 余り |
max / min | 最大値・最小値 |
evenp / oddp / zerop | 偶数・奇数・ゼロの判定 |
decf | 変数を1減らす |
count #\文字 s | 文字列中の文字の個数 |
char s i | 文字列の i 番目の文字 |
char-code / code-char | 文字と文字コードの変換 |
char-downcase | 文字を小文字にする |
copy-seq | 文字列をコピーする |
coerce ... 'string | リストを文字列に変換する |
loop for c across s collect ... | 文字列を1文字ずつ処理する |
list a b c | 値をリストにまとめる |
sort リスト #'< | リストを昇順に並べる |
first / third | リストの要素を取り出す |
length | リストや文字列の長さ |
remove-duplicates | 重複を取り除く |
apply #'min / apply #'max | リストの最小・最大 |