F - Teleporter Takahashi Editorial by kyopro_friends


簡潔な実装を目指して頑張って考察する方針の解説です。

\((x,y)\) にいる高橋君を \((p,q)\) に関して移動させると \((2p-x,2q-y)\) に移ります。この操作では \(x\) 座標 \(y\) 座標とも parity が変化しないことから、移動可能であるためには、mod 2 で \(s_x \equiv g_x\) , \(s_y \equiv g_y \) となっていることが必要です。

逆にこの条件が満たされているとき、\(a\neq b\) ならば\((a,c),(a+1,c)\) の順に操作を行うことで、\((x,y)\) から \((x+2,y)\) へ、\((a+1,c),(a,c)\) の順に操作を行うことで \((x-2,y)\) に移動することができます。\(y\) 座標に関しても \(c\neq d\) ならば 同様であるため、(操作回数の制限を無視すれば)、必ず移動可能であることがわかります。(操作回数の制限に抵触しないことは少し考えるとわかります)

\(a=b\) のとき、操作を行う回数のparityは \(s_x=g_x\) であるかどうかのみで決まります。上で述べた操作は全て2回1組であることから、「\(a=b\) かつ \(s_x\neq g_x\) ならまず1回操作する」としてから上で述べた操作を \(y\) 座標に関して行うとしてよいです。\(c=d\) の場合も同様です。

以上で問題を解くことができましたが、さらに少し考えると、\(s_x \equiv g_x\) かどうかを調べることなく「現在地が目的地より小さいなら+2」をやれるだけやったあと、「現在地が目的地より大きいなら-2」をやれるだけやって、目的地にたどり着けたかどうかを調べれば十分です。以上を踏まえたアルゴリズムは次のようになります。

(Python 風擬似コード)

  pos=(sx,sy)
  goal=(gx,gy)
  # flip(p,q) は pos を (p,q) に関して対称な位置に移す
  if a==b and pos[0]!=goal[0]: flip(a,c)
  if c==d and pos[1]!=goal[1]: flip(a,c)
  
  if a!=b:
    while pos[0]<goal[0]: flip(a,c),flip(a+1,c)
    while pos[0]>goal[0]: flip(a+1,c),flip(a,c)
  if c!=d:
    while pos[1]<goal[1]: flip(a,c),flip(a,c+1)
    while pos[1]>goal[1]: flip(a,c+1),flip(a,c)

  if pos==goal:
    print("Yes")
  else:
    print("No")

flip操作の履歴を記録しておけば、具体的な操作列も求めることができます。

実装例(C)

posted:
last update: