LoginSignup
5
0

More than 5 years have passed since last update.

BitcoinのスクリプトでAtCoderの問題を解いてみた

Posted at

本日(2018年6月2日)、競技プログラミングコンテストcodeFlyerが開催される。仮想通貨取引所を運営するbitFlyerの主催なので、きっと、使用可能な言語はBitcoinの取引に使用されているスクリプトだけだろう。ということで練習をした。

現在のAtCoderで使用可能な言語にBitcoinのスクリプトが無いので、Pythonのインタプリタを実装した。Input:に書かれた形式で標準入力から入力を読み取ってスタックに積み、Code:のスクリプトを実装して、Output:の形式で標準出力に書き出す。

AtCoder Beginners Selectionに挑戦した。初心者向けの簡単な問題が揃っている。

はじめてのあっとこーだー(Welcome to AtCoder)

sには手を付けず、a+b+cを実行すれば良い。

Input: int int int str
Output: int str
Code:
  OP_ADD
  OP_ADD

Product

問題文の通りに実装するだけ。

Input: int int
Output: str
Code:
  OP_MUL
  2
  OP_SWAP
  OP_MOD
  OP_IF
    Odd
  OP_ELSE
    Even
  OP_ENDIF

Placing Marbles

問題文ではs1, s2, s3は文字のようだが、まとめて整数として読みこむことにした。整数sとして読みこんでしまえば、答えはs%10+s/10%10+s/10/10となる。入力が8通りしかないので、単純に比較するという手もある。

Input: int
Output: int
Code:
  OP_DUP
  10
  OP_SWAP
  OP_MOD
  OP_SWAP

  10
  OP_SWAP
  OP_DIV

  OP_DUP
  10
  OP_SWAP
  OP_MOD
  OP_SWAP

  10
  OP_SWAP
  OP_DIV

  OP_ADD
  OP_ADD

Otoshidama

これがなかなか難しかった。

以降では、Yを最初に1,000で割ってしまい、10円札と5円札、1円札の枚数を考える。

o...............................................................................
.o...o....o.....................................................................
..o...o...oo...o....o...........................................................
...o...o...oo..oo...oo...o....o.................................................
....o...o...oo..oo..ooo..oo...oo...o....o.......................................
.....o...o...oo..oo..ooo.ooo..ooo..oo...oo...o....o.............................
......o...o...oo..oo..ooo.ooo.oooo.ooo..ooo..oo...oo...o....o...................
.......o...o...oo..oo..ooo.ooo.oooooooo.oooo.ooo..ooo..oo...oo...o....o.........
........o...o...oo..oo..ooo.ooo.ooooooooooooooooo.oooo.ooo..ooo..oo...oo...o....
.........o...o...oo..oo..ooo.ooo.oooooooooooooooooooooooooo.oooo.ooo..ooo..oo...
..........o...o...oo..oo..ooo.ooo.ooooooooooooooooooooooooooooooooooo.oooo.ooo..
...........o...o...oo..oo..ooo.ooo.oooooooooooooooooooooooooooooooooooooooooooo.

こんな感じで、どのNYなら可能なのだろうかとか考えたところ、次のような処理で答えが得られることがわかった。

N,Y = map(int, raw_input().split())
Y /= 1000
x = (Y-N)/9-[0,3,2,1,0,3,2,1,0][(Y-N)%9]
y = [0,7,5,3,1,8,6,4,2][(Y-N)%9]
z = N-x-y
if x>= 0 and z>=0:
  print x, y, z
else:
  print -1, -1, -1

全てを1円札で支払うと、Y枚になる。鶴亀算の要領で5円札や10円札に変えると、それぞれ4枚や9枚紙幣の枚数が減る。すなわち、「xyを自然数(0は自然数)として、4x+9y=Y-Nを満たせますか?」という問題になる。可能な金額を減らさないようにしつつ、yを最小にすることを考えると、yは9周期で0, 7, 5, 3, 1, 8, 6, 4, 2, …となる。という感じだろうか。これをBitcoinのスクリプトで実装すると、

Input: int int
Output: int int int
Code:
  1000 OP_ROT OP_DIV
  OP_2DUP OP_SUB
  9 OP_OVER OP_MOD

  OP_DUP 0 OP_NUMEQUAL OP_IF 0 OP_ELSE
  OP_DUP 1 OP_NUMEQUAL OP_IF 7 OP_ELSE
  OP_DUP 2 OP_NUMEQUAL OP_IF 5 OP_ELSE
  OP_DUP 3 OP_NUMEQUAL OP_IF 3 OP_ELSE
  OP_DUP 4 OP_NUMEQUAL OP_IF 1 OP_ELSE
  OP_DUP 5 OP_NUMEQUAL OP_IF 8 OP_ELSE
  OP_DUP 6 OP_NUMEQUAL OP_IF 6 OP_ELSE
  OP_DUP 7 OP_NUMEQUAL OP_IF 4 OP_ELSE
  OP_DUP 8 OP_NUMEQUAL OP_IF 2 OP_ELSE
  OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF

  OP_OVER 0 OP_NUMEQUAL OP_IF 0 OP_ELSE
  OP_OVER 1 OP_NUMEQUAL OP_IF 3 OP_ELSE
  OP_OVER 2 OP_NUMEQUAL OP_IF 2 OP_ELSE
  OP_OVER 3 OP_NUMEQUAL OP_IF 1 OP_ELSE
  OP_OVER 4 OP_NUMEQUAL OP_IF 0 OP_ELSE
  OP_OVER 5 OP_NUMEQUAL OP_IF 3 OP_ELSE
  OP_OVER 6 OP_NUMEQUAL OP_IF 2 OP_ELSE
  OP_OVER 7 OP_NUMEQUAL OP_IF 1 OP_ELSE
  OP_OVER 8 OP_NUMEQUAL OP_IF 0 OP_ELSE
  OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF

  OP_2SWAP OP_DROP
  9 OP_SWAP OP_DIV OP_SUB

  OP_DUP OP_DUP OP_ABS OP_NUMEQUAL
  OP_IF
    OP_2OVER OP_DROP
    OP_3DUP OP_SUB OP_SUB

    OP_DUP OP_DUP OP_ABS OP_NUMEQUAL
    OP_IF
      OP_2SWAP
    OP_ELSE
      -1 -1 -1
    OP_ENDIF
  OP_ELSE
    -1 -1 -1
  OP_ENDIF

スタックの様子を加えると次のようになる。左側がスタックの先頭。

# N Y
1000 OP_ROT OP_DIV
# Y/1000 N
OP_2DUP OP_SUB
# Y/1000-N Y/1000 N
9 OP_OVER OP_MOD
# (Y/1000-N)%9 Y/1000-N Y/1000 N

OP_DUP 0 OP_NUMEQUAL OP_IF 0 OP_ELSE
OP_DUP 1 OP_NUMEQUAL OP_IF 7 OP_ELSE
OP_DUP 2 OP_NUMEQUAL OP_IF 5 OP_ELSE
OP_DUP 3 OP_NUMEQUAL OP_IF 3 OP_ELSE
OP_DUP 4 OP_NUMEQUAL OP_IF 1 OP_ELSE
OP_DUP 5 OP_NUMEQUAL OP_IF 8 OP_ELSE
OP_DUP 6 OP_NUMEQUAL OP_IF 6 OP_ELSE
OP_DUP 7 OP_NUMEQUAL OP_IF 4 OP_ELSE
OP_DUP 8 OP_NUMEQUAL OP_IF 2 OP_ELSE
OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF
# y (Y/1000-N)%9 Y/1000-N Y/1000 N

OP_OVER 0 OP_NUMEQUAL OP_IF 0 OP_ELSE
OP_OVER 1 OP_NUMEQUAL OP_IF 3 OP_ELSE
OP_OVER 2 OP_NUMEQUAL OP_IF 2 OP_ELSE
OP_OVER 3 OP_NUMEQUAL OP_IF 1 OP_ELSE
OP_OVER 4 OP_NUMEQUAL OP_IF 0 OP_ELSE
OP_OVER 5 OP_NUMEQUAL OP_IF 3 OP_ELSE
OP_OVER 6 OP_NUMEQUAL OP_IF 2 OP_ELSE
OP_OVER 7 OP_NUMEQUAL OP_IF 1 OP_ELSE
OP_OVER 8 OP_NUMEQUAL OP_IF 0 OP_ELSE
OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF
# [0,3,2,1,0,3,2,1,0][(Y-N)%9] y (Y/1000-N)%9 Y/1000-N Y/1000 N

OP_2SWAP OP_DROP
# Y/1000-N [0,3,2,1,0,3,2,1,0][(Y-N)%9] y Y/1000 N

9 OP_SWAP OP_DIV OP_SUB
# x y Y/1000 N

OP_DUP OP_DUP OP_ABS OP_NUMEQUAL
# x>=0 x y Y/1000 N
OP_IF
  OP_2OVER OP_DROP
  # N x y Y/1000 N
  OP_3DUP OP_SUB OP_SUB
  # z N x y Y/1000 N

  OP_DUP OP_DUP OP_ABS OP_NUMEQUAL
  # z>=0 z N x y Y/1000 N
  OP_IF
    OP_2SWAP
    # x y z N Y/1000 N
  OP_ELSE
    -1 -1 -1
  OP_ENDIF
OP_ELSE
  -1 -1 -1
OP_ENDIF

その他の問題

Bitcoinのスクリプトではループが書けないので、その他の問題は厳しい。入力のサイズに上限があるので、大量のコードを並べるということで解けなくはないかもしれないが……。Coinsはループを使わずに解く方法があるかもしれない。誰か解いてほしい。

参考

Atcoder Beginners Selectionを難解言語Pietで解いてみた - ベースメモ

5
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
0