0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

paizaやってみた #1 「マップの扱い3 - マップの判定・縦横斜めPHP編」(Bランク)

Last updated at Posted at 2025-06-11
<?php
//マップの扱い3

/* 問題文
マップの行数 H と列数 W とマップを表す H 行 W 列の文字列 S_1 ... S_H と y , x 座標 が与えられる
与えられた座標のマス(y, x) と、 (y, x) と同じ縦・横・斜めの列に存在する全てのマスについて、、
次の処理をおこなった後の盤面を出力してください。

マスに書かれている文字が "." の場合は "#" に、"#" の場合は "." に書き換える。

なお、マスの座標系は左上端のマスの座標を ( y , x ) = ( 0 , 0 ) とし、
下方向が y 座標の正の向き、右方向が x 座標の正の向きとします。
マス(y,x) と同じ斜めの列とは整数 a を用いて (y+a,x+a), (y+a,x-a), (y-a,x-a), (y-a,x+a) のいずれかで表されるマスの集合です。
*/

/* 以下、投稿者の回答例
行数:$h
列数:$w
与えられる文字列を格納する配列:$s[]
与えられる基準の座標(y,x)と格納する変数:($y,$x) 
左上端 = (0,0)
$cy:ループ内での現在のy座標の数値
$cx:ループ内での現在のx座標の数値

*/

//行数と列数の取得
$hw = explode(" ",rtrim(fgets(STDIN)));
$h = (int)$hw[0];
$w = (int)$hw[1];

$s = [];

//文字列取得
for($i = 0; $i < $h; $i++){
  $s[$i] = mb_str_split(rtrim(fgets(STDIN))); //$s[$i][$s[0], $s[1]...]
}

//座標の取得
$yx = explode(" ",rtrim(fgets((STDIN))));
$y = (int)$yx[0];
$x = (int)$yx[1];

//横方向の処理
for($cx = 0; $cx < $w; $cx++){ 
  if($s[$y][$cx] === "#"){
    $s[$y][$cx] = ".";
  }elseif($s[$y][$cx] === "."){
    $s[$y][$cx] = "#";
  }
}

//縦方向の処理
for($cy = 0; $cy < $h; $cy++){ 
  if($s[$cy][$x] === "#"){
    $s[$cy][$x] = ".";
  }elseif($s[$cy][$x] === "."){
    $s[$cy][$x] = "#";
  }
}

//左上から右下の処理
$diff = $y - $x; //この処理では$y-$xの値は常に一定になる
for($cy = 0; $cy < $h; $cy++){
  $cx = $cy - $diff; //

  if($cx >= 0 && $cx < $w){
    if($s[$cy][$cx] === "#"){
      $s[$cy][$cx] = ".";
    }elseif($s[$cy][$cx] === "."){
      $s[$cy][$cx] = "#";
    }
  }
}

//右上から左下の処理
$sum = $y + $x; //この処理では$y+$xの値は常に一定になる
for($cy = 0; $cy < $h; $cy++){
  $cx = $sum - $cy;

  if($cx >= 0 && $cx < $w){
    if($s[$cy][$cx] === "#"){
      $s[$cy][$cx] = ".";
    }elseif($s[$cy][$cx] === "."){
      $s[$cy][$cx] = "#";
    }
  }
}

//基準点の処理
if($s[$y][$x] === "#"){
  $s[$y][$x] = ".";
  }elseif($s[$y][$x] === "."){
    $s[$y][$x] = "#";
  }


for($i = 0; $i < $h; $i++){
    echo implode("", $s[$i]) . "\n";
}

変数名は適当につけてます。わかりづらくてすいません。💦

ざっくりした考え方は、

  1. 入力値の取得(paiza恒例)
  2. 基準座標からみて、同じ縦横斜めの列の値の変更と配列への格納
  3. 基準座標の値の変更と配列への格納
  4. 配列を結合し、文字列として出力
    となっています。

#1. 入力値の取得
詳しい入力値の処理はpaizaに載ってますので割愛します。

ここでの重要なポイントは、mb_str_splitを使用して、
文字列を構成する文字を配列として格納することです。
配列にすることで、変換したい文字を座標で指定することができます。

for($i = 0; $i < $h; $i++){
  $s[$i] = mb_str_split(rtrim(fgets(STDIN))); 
}
//$s[$y][$x]のような形で文字の座標を指定する

#2. 基準座標からみて、同じ縦横斜めの列の値の変更と配列への格納

//左上から右下の処理
$diff = $y - $x; //この処理では$y-$xの値は常に一定になる
for($cy = 0; $cy < $h; $cy++){
  $cx = $cy - $diff; //

  if($cx >= 0 && $cx < $w){
    if($s[$cy][$cx] === "#"){
      $s[$cy][$cx] = ".";
    }elseif($s[$cy][$cx] === "."){
      $s[$cy][$cx] = "#";
    }
  }
}

基準座標からみて縦、横、左上から右下、右上から左下でそれぞれ処理を作成する。
縦と横の処理は簡単でしたが、斜めの処理はかなり手こずりました笑
コツとしては、それぞれの座標を特定する式を考えることです。

座標の特定に使う式
$diff = $y - $x; //この処理では$y-$xの値は常に一定になる

紙にグリッドと実数値を書いて、シミュレーションするとわかりやすいです!

#3. 基準座標の値の変更と配列への格納

基準座標は縦、横、左上から右下、右上から左下それぞれで
文字が変更されるため、2の処理を終えた時点で入力値に戻っています。
そのため、最後に基準座標の文字を変更することで、配列への格納が
完了します。

#4. 配列を結合し、文字列として出力

ここで、先ほどまで格納した配列内の変更後の文字を、再度文字列として
再構成し、出力します。

for($i = 0; $i < $h; $i++){
    echo implode("", $s[$i]) . "\n";
}

以上がこの問題の解説でした。

この問題、paizaにPHPでの回答例がなく、何回も試行錯誤したり、ググったり、Geminiに聞いたりしてようやくたどり着きました。(この問題は練習問題なので、カンニングには当たりません。私の現ランクはCのままです。)

今回が初めての投稿でしたので、至らぬところあったと思いますが、
参考になれば幸いです。あとコメントいただけると嬉しいです。

0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?