ABC A問題で学ぶ Common Lisp 入門

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 / 2floor で整数として計算しています。
3つ目の辺 c は使わないので read で読み捨てています。

2.2. 問題 2 — ABC113A: Discount Fare

問題リンク

整数 XY が与えられます。
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/2floor で切り捨て除算しています。

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

問題リンク

整数 AB が与えられます。
A+BA-BA*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

問題リンク

整数 AB が与えられます。
A+BA-BA*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

問題リンク

整数 XA が与えられます。
XA 未満なら 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+BC+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!

問題リンク

整数 AB が与えられます。
どちらも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

問題リンク

整数 AB が与えられます。
BA の倍数なら 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

問題リンク

整数 AB が与えられます。
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

問題リンク

整数 AB が与えられます。
どちらも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 (条件11)
      (条件22)
      (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 の次は CloudyCloudy の次は RainyRainy の次は 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文字(SUNSAT)で与えられます。
次の日曜日まで何日かを出力してください。
日曜なら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

問題リンク

整数 NR が与えられます。
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

問題リンク

19 だけからなる3文字の文字列 N が与えられます。
19 に、91 に置き換えた文字列を出力してください。

解答

(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 が与えられます。
SK 文字目だけを小文字にして出力してください。

解答

(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

問題リンク

AB だけからなる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

問題リンク

日付文字列 SYYYY/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段階:計算の応用

floorceilingminmax を組み合わせて解く問題です。

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つの整数 AB が与えられます。
与えられていない残りの整数を出力してください。

解答

(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種類のボール名 ST、それぞれの個数 AB、取り出したボール名 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なら、続けて整数 AB が与えられるので、その和を出力してください。

解答

(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

問題リンク

整数 AB が与えられます。
1月1日、2月2日…のように月と日が同じ日を、AB 日までに何回迎えるかを出力してください。

解答

(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-line1行を文字列として読む
princ値を出力する
#\Space空白文字
terpri改行する
let / let*変数を作る
declare (ignore ...)使わない変数を無視する宣言
if2通りに分岐する
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リストの最小・最大