質問するログイン新規登録
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

1回答

286閲覧

AtCoder Typical Contest 001 の A問題について

HolmesTG

総合スコア2

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2023/11/06 09:54

編集2023/11/06 10:18

0

0

実現したいこと

  • ACが通るプログラムに修正する。
  • WAになってしまう理由を理解する。

前提

AtCoderコンテスト ATC001 の A問題「深さ優先探索」の問題にチャレンジしています。
スタックを使用する等、他の解法もありますがこの質問では「なぜこのプログラムだとWAなのか」その理由を教えて欲しいです。

発生している問題・エラーメッセージ

唯一、テストケース「01_rnd_10.txt」だけWA判定になります。他のテストケースは全てAC判定です。

該当のソースコード

cpp

1#include<bits/stdc++.h> 2using namespace std; 3#define ll long long 4#define rep(i,n) for(int i = 0; i < (n); ++i) 5 6char c[500][500]; 7 8void DFS(int x, int y, int limX, int limY, bool& ans){ 9 10 if(c[y][x]=='g') { // ゴールに来た 11 ans = true; 12 return; 13 }else if(c[y][x]=='#' || x<0 || x>=limX || y<0 || y>=limY || c[y][x]=='*'){ // 壁or外or既出 14 return; 15 } 16 17 c[y][x] = '*'; 18 19 DFS(x, y-1, limX, limY, ans); 20 DFS(x+1, y, limX, limY, ans); 21 DFS(x, y+1, limX, limY, ans); 22 DFS(x-1, y, limX, limY, ans); 23} 24 25int main(){ 26 int h, w; 27 cin >> h >> w; 28 rep(i,h) rep(j,w) cin >> c[i][j]; 29 30 bool ans = false; 31 32 rep(i,h) rep(j,w) { 33 if(c[i][j] == 's') DFS(j,i,w,h,ans); 34 } 35 36 cout << (ans ? "Yes" : "No") << endl; 37 return 0; 38}

試したこと

ChatGPTやbingAIに質問しましたが、期待していた出力は得られませんでした。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fana

2023/11/06 10:15

(この手のどこぞの問題の話を持ってくる人って,何故問題へのリンクを示さないのだろう?)
fana

2023/11/06 10:28

> 01_rnd_10.txt とかいうやつの内容をどこから見れるのか探せなかったが,それはそれとして, 関数 DFS() の実装はこれだと範囲外参照しまくりなのでは? と見える (:(x,y) が範囲内にあるか否かの判定よりも先に c[y][x] にアクセスしてる)ので, 他の入力例で正解になる方が「たまたま」なのではなかろうか.
fana

2023/11/06 10:30

あと,ゴールが見つかっても探索を打ち切らないのは効率悪そう.
fana

2023/11/06 10:34

> 先に c[y][x] にアクセスしてる 念のために言っておくが 先頭のゴールの判定だけじゃなくて,その後の else if もだよ. (後者側がピンとこない場合には 「短絡評価(ショートサーキット)」 とかでググると良いかと)
HolmesTG

2023/11/06 10:53

範囲外判定をDFS()の最初に行うように変更したらACになりました...。 とても納得できました、ありがとうございます! 範囲外参照しっかり意識したいと思います...。 ちなみになのですが、この書き方の様な再帰関数の場合、ゴールが見つかった時点で他の探索を打ち切るというのは、DFS()の先頭に if(ans) return; を書いておけば良いという認識で良いでしょうか?
fana

2023/11/07 02:00 編集

> DFS()の先頭に if(ans) return; を書いておけば良い とりあえずそれで良いと思います. それはそれとして, 「ans を引数にとってそれを書き換える」という形にする必要が特段無いならば,結果を return で返す形の関数とするほうが素直(?)であるように思います. で,DFS() 内末尾での4つの DFS() 呼び出しのところを return ( DFS(x,y-1) || DFS(x+1,y) || DFS(x,y+1) || DFS(x-1,y) ); のように書くとか.(ここもまたショートサーキットになるので,いずれかがtrueを返してきた時点で後ろ側のDFS()呼び出しは成されない:探索の打ち切り)
HolmesTG

2023/11/07 06:43

ありがとうございます。勉強になりました。
fana

2023/11/07 07:03

問題が解決した場合には,この質問を何かしら適切な形で解決状態にしてください. (ヘルプを見るに,「回答」が付いていない段階で問題が解決した場合には自己解決の形をとるのが良い様子です)
guest

回答1

0

自己解決

コメントで原因をご指摘いただき、修正することでACを通すことができました。大変勉強になりました。ありがとうございました。

WAの原因

DFS()内で、配列の範囲外参照チェックをする前に配列にアクセスしていたこと。

ACのソースコード

cpp

1#include<bits/stdc++.h> 2using namespace std; 3#define ll long long 4#define rep(i,n) for(int i = 0; i < (n); ++i) 5 6char c[500][500]; 7 8bool DFS(int x, int y, int limX, int limY){ 9 10 if(x<0 || x>=limX || y<0 || y>=limY){ // 外 11 return false; 12 }else if(c[y][x]=='g') { // ゴールに来た 13 return true; 14 }else if(c[y][x]=='#' || c[y][x]=='*'){ // 壁or既出 15 return false; 16 } 17 18 c[y][x] = '*'; 19 20 return (DFS(x, y-1, limX, limY) || DFS(x+1, y, limX, limY) || DFS(x, y+1, limX, limY) || DFS(x-1, y, limX, limY) ); 21} 22 23int main(){ 24 int h, w; 25 cin >> h >> w; 26 rep(i,h) rep(j,w) cin >> c[i][j]; 27 28 bool ans = false; 29 30 rep(i,h) rep(j,w) { 31 if(c[i][j] == 's') ans = DFS(j,i,w,h); 32 } 33 34 cout << (ans ? "Yes" : "No") << endl; 35 return 0; 36}

投稿2023/11/07 08:01

HolmesTG

総合スコア2

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.30%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問