AtCoder に登録したら解くべき精選過去問 10 問 を なでしこ で解いてみた

# はじめに

意外にも @drkenさんの記事 **[AtCoder に登録したら次にやること ～ これだけ解けば十分闘える！過去問精選 10 問 ～](https://qiita.com/drken/items/fd4e5e3630d0f5859067)** に **[なでしこ](https://nadesi.com/top/)** が無かったので解いてみました。

**なるべく自然言語としての日本語に近い** 記載を意識した解答にしています。
そのため、原題の日本語も合わせて掲載しています。

なお、本記事ではじめて **なでしこ** を使用したため、誤りがある場合はコメントや編集リクエストで優しく教えてください。

# なでしこ について

なでしこは **「日本語で誰でも簡単プログラマ−」** をモットーとしているプログラミング言語です

本記事では、**[なでしこ](https://nadesi.com/top/)** の説明は割愛しますが、**v3** は **[altJS](https://nadesi.com/doc3/index.php?%E3%81%AA%E3%81%A7%E3%81%97%E3%81%933%E3%81%AE%E5%8B%95%E4%BD%9C%E5%8E%9F%E7%90%86)** という側面があります。

(そのため、今回の記事作成では、@ytantoさんの **[AtCoder に登録したら解くべき精選過去問 10 問を JavaScript で解いてみた](https://qiita.com/ytanto/items/caf7bf0ba287da81b20f)** を大いに参考にしました。)

なお、本記事のコードは **[なでしこ3 Web簡易エディタ](https://nadesi.com/doc3/index.php?%E3%81%AA%E3%81%A7%E3%81%97%E3%81%933%E7%B0%A1%E6%98%93%E3%82%A8%E3%83%87%E3%82%A3%E3%82%BF)** にて確認しています。

# 標準入出力

**なでしこ は AtCoder で使用できるプログラミング言語ではありません。**

**[なでしこ3 Web簡易エディタ](https://nadesi.com/doc3/index.php?%E3%81%AA%E3%81%A7%E3%81%97%E3%81%933%E7%B0%A1%E6%98%93%E3%82%A8%E3%83%87%E3%82%A3%E3%82%BF)** では**標準入出力の扱いができない**ため、本記事では **尋ねる** 命令を使用します。

``` nako
尋ねる。
それを表示する。
```

ただ、**altJSとして、 [AtCoderのJavaScritの標準入出力の扱い](https://qiita.com/ytanto/items/caf7bf0ba287da81b20f#%E6%A8%99%E6%BA%96%E5%85%A5%E5%87%BA%E5%8A%9B)にそろえたい場合**は、下記のように記載できます。

``` nako
「/dev/stdin」から開く。
それを表示する。
```

# 第1問: [ABC 086 A - Product](https://beta.atcoder.jp/contests/abc086/tasks/abc086_a)

シカのAtCoDeerくんは二つの正整数 $a$ , $b$ を見つけました。 $a$ と $b$ の積が偶数か奇数か判定してください。

(二つの整数 $a$ , $b$ の積の偶奇を判定する。)


``` nako
尋ねる
入力はそれを「 」で区切る
aは入力[0]のINT
bは入力[1]のINT
aにbを掛けて2で割った余り
もし、それが0ならば
「Even」を表示する
違えば
「Odd」を表示する
ここまで
```

# 第2問: [ABC 081 A - Placing Marbles](https://beta.atcoder.jp/contests/abc081/tasks/abc081_a)

すぬけ君は $1$ , $2$ ,  $3$ の番号がついた $3$ つのマスからなるマス目を持っています。 各マスには **'0'** か **'1'** が書かれており、マス $i$ には $s_i$ が書かれています。
すぬけ君は **'1'** が書かれたマスにビー玉を置きます。 ビー玉が置かれるマスがいくつあるか求めてください。

(文字列内の、**'1'** の個数をカウントする。)

``` nako
尋ねる
それを「1」で区切る
それの要素数から1を引いて表示する
```

# 第3問: [ABC 081 B - Shift Only](https://beta.atcoder.jp/contests/abc081/tasks/abc081_b)

黒板に $N$ 個の正の整数 $A_1$ , $\cdots$ ,$A_N$ が書かれています．
すぬけ君は，黒板に書かれている整数がすべて偶数であるとき，次の操作を行うことができます．

 - 黒板に書かれている整数すべてを，$2$ で割ったものに置き換える．

すぬけ君は最大で何回操作を行うことができるかを求めてください．

( $N$ 個の整数を同時に、割り切れなくなるまで割っていった場合、何回割ることができるかを求める。)

``` nako
尋ねる
尋ねる
入力はそれを「 」で区切る
操作数は0
OKの間
  奇数存在フラグはNG
　入力の要素数から1を引く
  インデックスを0からそれまで繰り返す
    もし、(入力[インデックス]を2で割った余り)が0でなければ
　　  奇数存在フラグはOK
    ここまで
    入力[インデックス]を2で割る
    入力[インデックス]はそれを切り捨て
  ここまで
  もし、奇数存在フラグがOKならば
　  抜ける
  ここまで
  操作数は操作数に1を足す
ここまで
操作数を表示する
```

# 第4問: [ABC 087 B - Coins](https://beta.atcoder.jp/contests/abc087/tasks/abc087_b)

あなたは、$500$ 円玉を $A$ 枚、$100$ 円玉を $B$ 枚、$50$ 円玉を $C$ 枚持っています。 これらの硬貨の中から何枚かを選び、合計金額をちょうど $X$ 円にする方法は何通りありますか。

同じ種類の硬貨どうしは区別できません。2 通りの硬貨の選び方は、ある種類の硬貨についてその硬貨を選ぶ枚数が異なるとき区別されます。

($500$ 円玉、$100$ 円玉、$50$ 円玉を使って $X$ 円を支払う方法が何通りあるかを求める。)

``` nako
尋ねる
A=それのINT
尋ねる
B=それのINT
尋ねる
C=それのINT
尋ねる
X=それのINT
パターン=0
aを0からAまで繰り返す
  bを0からBまで繰り返す
    cを0からCまで繰り返す
      合計=0
      aに500を掛ける
      合計=合計にそれを足す
      bに100を掛ける
      合計=合計にそれを足す
      cに50を掛ける
      合計=合計にそれを足す
      もし、合計がXならば
        パターン=パターンに1を足す
      ここまで。
    ここまで。
  ここまで。
ここまで。
パターンを表示する
```

# 第5問: [ABC 083 B - Some Sums](https://beta.atcoder.jp/contests/abc083/tasks/abc083_b)

$1$ 以上 $N$ 以下の整数のうち、 $10$ 進法での各桁の和が $A$ 以上 $B$ 以下であるものの総和を求めてください。

``` nako
尋ねる
入力値=それを「 」で区切る
N=入力値[0]のINT
A=入力値[1]のINT
B=入力値[2]のINT
総和=0
繰返数を1からNまで繰り返す
  一時数=繰返数
  一時桁和=0
  一時数が1以上の間
    一時数を10で割った余り
    一時桁和=一時桁和+それ
    一時数を10で割る
    一時数=それを切り捨て
  ここまで
  もし、AND(一時桁和>=A,一時桁和<=B)ならば
    総和=総和+繰返数
  ここまで
ここまで
総和を表示する
```

# 第6問: [ABC 088 B - Card Game for Two](https://beta.atcoder.jp/contests/abc088/tasks/abc088_b)

$N$ 枚のカードがあります. $i$ 枚目のカードには, $a_i$ という数が書かれています.
Alice と Bob は, これらのカードを使ってゲームを行います. ゲームでは, Alice と Bob が交互に 1 枚ずつカードを取っていきます. Alice が先にカードを取ります.
2 人がすべてのカードを取ったときゲームは終了し, 取ったカードの数の合計がその人の得点になります. 2 人とも自分の得点を最大化するように最適な戦略を取った時, Alice は Bob より何点多く取るか求めてください.

($N$ 枚のカードを交互に自身の得点が最大になるように取っていった場合の、二人の得点の差分を求める。)

``` nako
尋ねる
N=それのINT
尋ねる
カード札=それを「 」で区切る
ボブ得点=0
アリス得点=0
カード札を配列数値ソート
カード札を配列逆順
カード札の要素数から1を引く
インデックスを0からそれまで繰り返す
  カード=カード札[インデックス]のINT
  インデックスを2で割った余り
  もし、それが0ならば
    ボブ得点=ボブ得点+カード
  違えば
    アリス得点=アリス得点+カード
  ここまで
ここまで
解答=ボブ得点からアリス得点を引く
解答を表示する
```

# 第7問: [ABC 085 B - Kagami Mochi](https://beta.atcoder.jp/contests/abc085/tasks/abc085_b)

$X$ 段重ねの鏡餅 $(X≥1)$ とは、$X$ 枚の円形の餅を縦に積み重ねたものであって、どの餅もその真下の餅より直径が小さい（一番下の餅を除く）もののことです。例えば、直径 $10$、$8$、$6$ センチメートルの餅をこの順に下から積み重ねると $3$ 段重ねの鏡餅になり、餅を一枚だけ置くと $1$ 段重ねの鏡餅になります。

ダックスフンドのルンルンは $N$ 枚の円形の餅を持っていて、そのうち $i$ 枚目の餅の直径は $d_i$ センチメートルです。これらの餅のうち一部または全部を使って鏡餅を作るとき、最大で何段重ねの鏡餅を作ることができるでしょうか。

($N$ 個の整数の重複しない値の数を求める。)

``` nako
尋ねる
N=それのINT
鏡餅={}
(N)回
  尋ねる
  鏡餅[それ]=「真」
ここまで
鏡餅のハッシュキー列挙の要素数を表示する
```

# 第8問: [ABC 085 C - Otoshidama](https://beta.atcoder.jp/contests/abc085/tasks/abc085_c)

日本でよく使われる紙幣は、$10000$ 円札、$5000$ 円札、$1000$ 円札です。以下、「お札」とはこれらのみを指します。
青橋くんが言うには、彼が祖父から受け取ったお年玉袋にはお札が $N$ 枚入っていて、合計で $Y$ 円だったそうですが、嘘かもしれません。このような状況がありうるか判定し、ありうる場合はお年玉袋の中身の候補を一つ見つけてください。なお、彼の祖父は十分裕福であり、お年玉袋は十分大きかったものとします。

($10000$ 円札、$5000$ 円札、$1000$ 円札がある前提で、合計が $N$ 枚、$Y$ 円になる組み合わせを求める。)

``` nako
尋ねる
入力=それを「 」で区切る
全札数=入力[0]のINT
合計金額=入力[1]のINT
結果＝「-1 -1 -1」
一万円札数を0から全札数まで繰り返す
  五千円札数を0から(全札数-一万円札数)まで繰り返す
    一時合計=一万円札数に10000を掛ける
    五千円札数に5000を掛ける
    一時合計=一時合計にそれを足す
    千円札数=全札数から(一万円札数に五千円札数を足す)を引く
    千円札数に1000を掛ける
    一時合計=一時合計にそれを足す
    もし、一時合計が合計金額ならば
      結果＝「{一万円札数} {五千円札数} {千円札数}」
    ここまで
  ここまで
ここまで
結果を表示する
```

# 第9問: [ABC 049 C - Daydream](https://beta.atcoder.jp/contests/abc049/tasks/arc065_a)

英小文字からなる文字列 $S$ が与えられます。 $T$が空文字列である状態から始め、以下の操作を好きな回数繰り返すことで $S=T$ とすることができるか判定してください。

 - $T$ の末尾に **'dream'** **'dreamer'** **'erase'** **'eraser'** のいずれかを追加する。

(文字列 $S$ が、**'dream'**, **'dreamer'**, **'erase'**, **'eraser'** の組み合わせで作れるかどうかを判定する。)

``` nako
尋ねる
入力=それ
逆読=それの文字列分解の配列逆順を「」で配列結合
逆読文字数=逆読の文字数
ワードリスト=[]
ワードリスト[0]=「dream」の文字列分解の配列逆順を「」で配列結合
ワードリスト[1]=「dreamer」の文字列分解の配列逆順を「」で配列結合
ワードリスト[2]=「erase」の文字列分解の配列逆順を「」で配列結合
ワードリスト[3]=「eraser」の文字列分解の配列逆順を「」で配列結合
(逆読文字数>0)の間
  一致フラグ=「偽」
  ワードでワードリストを反復
    逆読でワードが何文字目
    もし、それが1ならば
      逆読=逆読の1からワードの文字数だけ文字削除
      逆読文字数=逆読の文字数
      一致フラグ=「真」
      抜ける
    ここまで
  ここまで
  もし、一致フラグが「偽」ならば
    抜ける
  ここまで
ここまで
もし、逆読文字数が0ならば
  「YES」と表示
違えば
  「NO」と表示
ここまで
```

# 第10問: [ABC 086 C - Traveling](https://beta.atcoder.jp/contests/abc086/tasks/arc089_a)

シカのAtCoDeerくんは二次元平面上で旅行をしようとしています。 AtCoDeerくんの旅行プランでは、時刻 $0$ に 点 $(0,0)$ を出発し、 $1$ 以上 $N$ 以下の各 $i$ に対し、時刻 $t_i$ に 点 $(x_i,y_i)$ を訪れる予定です。
AtCoDeerくんが時刻 $t$ に 点 $(x,y)$ にいる時、 時刻 $t+1$ には 点 $(x+1,y), (x−1,y), (x,y+1), (x,y−1)$ のうちいずれかに存在することができます。 その場にとどまることは出来ないことに注意してください。 AtCoDeerくんの旅行プランが実行可能かどうか判定してください。

(座標 $(0, 0)$ を出発地点とした場合、時刻 $t_i$ 時点で $(x_i, y_i)$にいることができるかどうかを判定する。)

``` nako
尋ねる
N=それのINT
時間位置=[ {t: 0, x: 0, y: 0} ]
(N)回
  尋ねる
  入力=それを「 」で区切る
  一時時間位置= {}
  一時時間位置[「t」]=入力[0]のINT
  一時時間位置[「x」]=入力[1]のINT
  一時時間位置[「y」]=入力[2]のINT
  時間位置に一時時間位置を配列追加
ここまで
移動可能="Yes"
インデックスを1からNまで繰り返す
  時間差異=時間位置[インデックス][「t」]から時間位置[インデックス-1][「t」]を引く
  X差異=時間位置[インデックス][「x」]から時間位置[インデックス-1][「x」]を引く
  X差異=X差異のABS
  Y差異=時間位置[インデックス][「y」]から時間位置[インデックス-1][「y」]を引く
  Y差異=Y差異のABS
  距離差異=X差異にY差異を足す
  もし、(時間差異 < 距離差異)ならば
    移動可能="No"
  ここまで
  時間差異偶奇=時間差異を2で割った余り
  距離差異偶奇=距離差異を2で割った余り
  もし、時間差異偶奇が距離差異偶奇でなければ
    移動可能="No"
  ここまで
ここまで
移動可能を表示する
```

# さいごに

**なでしこ** を使用して、 **競技プログラミング** に参加する場合は、自然言語としての日本語に近い記載ではなく、もっと演算子や計算式を使用したほうが効率はいい(書きやすい)と思います

しかし、自然言語としての日本語に近ければ近いほど、プログラミングが分からない人でも **競技プログラミング** の雰囲気をつかめるのではないかと、可能性も感じました。

今回、はじめて **なでしこ** を触ったため、連文等を使用すると、もっと自然言語としての日本語に近い記載が可能とは思いますが、まずは記事を公開し、随時修正していこうと思っています。