<?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";
}
変数名は適当につけてます。わかりづらくてすいません。💦
ざっくりした考え方は、
- 入力値の取得(paiza恒例)
- 基準座標からみて、同じ縦横斜めの列の値の変更と配列への格納
- 基準座標の値の変更と配列への格納
- 配列を結合し、文字列として出力
となっています。
#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のままです。)
今回が初めての投稿でしたので、至らぬところあったと思いますが、
参考になれば幸いです。あとコメントいただけると嬉しいです。