質問をすることでしか得られない、回答やアドバイスがある。

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

新規登録して質問してみよう
ただいま回答率
85.46%
C++

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

Q&A

解決済

2回答

1053閲覧

部分和問題の深さ優先探索

reo517

総合スコア1

C++

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

0グッド

0クリップ

投稿2020/04/27 17:12

c++を使って、部分和問題を深さ優先探索で書いています。
その際の再帰関数が分かりにくいのでご教授ください。
例は蟻本のp33です
bool dfs(int i ,int sum){
if(i==n) return sum == k;
// n個決め終わったら、sumがkと等しいかを返す
★sumが等しいかを返すならtrue or falseではないのですか?
★returnで何が返ってくるのかわかりません
if(dfs(i+1,sum)) return true;
★dfs(i,sum)はtrue なんですか?falseなんですか? 
★何をもってdfs(i+1,sum)の値が決まるのかがこの式から読めません。
if(dfs(i+1),sum+a[i])) return true;
return false;
}
void solve(){
if(dfs(0,0)) printf("Yes\m");
★なぜ0,0なんですか?

素人質問ですみません。いろいろ調べまわりましたが、部分和のDFSについて詳しい説明がありませんでした。
何かわかりやすい解説があれば教えてください。よろしくお願いします。

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

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

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

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

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

guest

回答2

0

ベストアンサー

★sumが等しいかを返すならtrue or falseではないのですか?
★returnで何が返ってくるのかわかりません

まず sum == k が「式」であることを理解しましょう。

式というのはプログラムにおいては評価され置換されるもののことを指します。以下が例です。(iは宣言済みとします。)

cpp

11 + 1 22 * 2 3i 4i++ 5++i

iに1が入っていれば、上から順に 24112 の値に置き換わります。

sum == k も同様に式です。この式は sumk の値が同じであれば true、 違う場合は false に置き換わります。

if文の引数として渡される式に対しても、この「評価」が行われています。変数がくると変数も評価され値が置き換わります。よって以下のような書き方が可能です。

cpp

1#include <iostream> 2 3int main() { 4 5 bool hoge = 1 == 2; // hogeにfalseが代入される 6 7 if (hoge) { // hogeの評価値はfalseなのでelse以降を実行 8 printf("同じ\n"); 9 } else { 10 printf("違う\n"); 11 } 12 13 return 0; 14}

今回の場合、return文によって k == sum の評価された値が、dfs関数の返り値となります。したがって、 true または false の値が返されます。

★dfs(i,sum)はtrue なんですか?falseなんですか?
★何をもってdfs(i+1,sum)の値が決まるのかがこの式から読めません。

まず動くソースコードを。コメント、つけ直してみました(大して変わってないかも)
○1, ○2, ○3, ○4はソースコード以下の説明と対応しています。

wandbox: https://wandbox.org/permlink/AMrTofmWNM2RKDpA

cpp

1#include <iostream> 2 3const int MAX_N = 20; 4int *a; 5int n, k; 6 7bool dfs(int i, int sum) { 8 /* 9 この再帰関数の深さがn(今回は4)であれば、最終的に合計値(sum)が目的の値(k...今回なら13) 10 になっているかを返して終わり。 11 */ 12 if (i == n) return sum == k; // ○1 13 14 /* 15 まだ深さが4ではないときに以下は実行される。 16 もしa[i]を合計値に入れずに目的の値になったならば、真を返す。 17 */ 18 if (dfs(i + 1, sum)) return true; // ○2 19 20 /* 21 もしa[i]を合計値に入れて目的の値になったならば、真を返す。 22 */ 23 if (dfs(i + 1, sum + a[i])) return true; // ○3 24 25 /* 26 a[i]を入れようが入れまいが、もう合計値にすることは不可能であるからfalseを返す。 27 */ 28 return false; // ○4 29} 30 31void solve() { 32 if (dfs(0, 0)) printf("Yes\n"); 33 else printf("No\n"); 34} 35 36int main(void) { 37 n = 4; 38 int b[MAX_N] = {1, 2, 4, 7}; 39 a = b; 40 k = 13; 41 42 solve(); // Yes 43}

i の役割は配列の何番目の話をしているかで、 sum の役割は再帰関数がうまく働くようにするための蓄積値といったところです。

この関数は次のように動きます。( https://wandbox.org/permlink/VikAMasmuqES69RS で確認できます。以下の説明文めちゃくちゃ長いですが、実際に動きを追ったほうがわかりやすいので書いてみました。)


dfs(0, 0)が呼ばれる。

○1 i ≠ nであるから、まだ値は返さない。
○2 dfs(1, 0)が呼ばれる。

○1 i ≠ nであるから、まだ値は返さない。
○2 dfs(2, 0)が呼ばれる。

○1 i ≠ nであるから、まだ値は返さない。
○2 dfs(3, 0)が呼ばれる。

○1 i ≠ nであるから、まだ値は返さない。
○2 dfs(4, 0)が呼ばれる。

○1 i = nであるから、sum == k の式の評価値を返す。 0 == 13 は偽なので、 dfs(4, 0)false を返す。

○3 dfs(3, 0) の中に戻る。○2 が実行されなかったので、次に sum(=0)a[3](=7) が足された
dfs(4, 7)が呼ばれる。

○1 i = nであるから、sum == k の式の評価値を返す。 7 == 13 は偽なので、 dfs(4, 7)false を返す。

○4 dfs(3, 0) の中に戻る。 dfs(3, 0)false を返す。

○3 dfs(2, 0) の中に戻る。○2 が実行されなかったので、次に sum(=0)a[2](=4) が足された
dfs(3, 4)が呼ばれる。

○1 i ≠ nであるから、まだ値は返さない。
○2 dfs(4, 4)が呼ばれる。

○1 i = nであるから、sum == k の式の評価値を返す。 4 == 13 は偽なので、 dfs(4, 4)false を返す。

○3 dfs(3, 4) の中に戻る。○2 が実行されなかったので、次に sum(=4)a[3](=7) が足された
dfs(4, 11)が呼ばれる。

○1 i = nであるから、sum == k の式の評価値を返す。 11 == 13 は偽なので、 dfs(4, 11)false を返す。

○4 dfs(3, 4) の中に戻る。 dfs(3, 4)false を返す。

○4 dfs(2, 0) の中に戻る。 dfs(2, 0)false を返す。

○3 dfs(1, 0) の中に戻る。○2 が実行されなかったので、次に sum(=0)a[1](=2) が足された
dfs(2, 2)が呼ばれる。

○1 i ≠ nであるから、まだ値は返さない。
○2 dfs(3, 2)が呼ばれる。

○1 i ≠ nであるから、まだ値は返さない。
○2 dfs(4, 2)が呼ばれる。

○1 i = nであるから、sum == k の式の評価値を返す。 2 == 13 は偽なので、 dfs(4, 2)false を返す。

○3 dfs(3, 2) の中に戻る。○2 が実行されなかったので、次に sum(=2)a[3](=7) が足された
dfs(4, 9)が呼ばれる。

○1 i = nであるから、sum == k の式の評価値を返す。 9 == 13 は偽なので、 dfs(4, 9)false を返す。

○4 dfs(3, 2) の中に戻る。 dfs(3, 2)false を返す。

○3 dfs(2, 2) の中に戻る。○2 が実行されなかったので、次に sum(=2)a[2](=4) が足された
dfs(3, 6)が呼ばれる。

○1 i ≠ nであるから、まだ値は返さない。
○2 dfs(4, 6)が呼ばれる。

○1 i = nであるから、sum == k の式の評価値を返す。 6 == 13 は偽なので、 dfs(4, 6)false を返す。

○3 dfs(3, 6) の中に戻る。○2 が実行されなかったので、次に sum(=6)a[3](=7) が足された
dfs(4, 13)が呼ばれる。

○1 i = nであるから、sum == k の式の評価値を返す。 13 == 13 は真なので、 dfs(4, 13)true を返す。

○3のif文以降が実行される。 dfs(3, 6)true を返す。

○3のif文以降が実行される。 dfs(2, 2)true を返す。

○3のif文以降が実行される。 dfs(1, 0)true を返す。

○2のif文以降が実行される。 dfs(0, 0)true を返す。


この説明は蟻本p34の解説絵と同じです。

★なぜ0,0なんですか?

dfs関数の再帰の仕組みを理解すれば自ずとわかるかと思います。

i = 0なのは、配列の0番目から部分和を確認する必要があるためです。これは明白だと思います。(i = 4であればi = 0, 1, 2, 3は無視される...明らかにそれは望んだ操作ではない)

sum = 0なのは、再帰関数がうまく働くようにするには最初の値を0にする必要があるからというのがわかると思います。この変数は、部分和が目的値になっているかを確認するために使われています。

遷移について等、まだよくわからない部分があれば返信ください。m(_ _)m

投稿2020/04/27 18:55

編集2020/04/27 19:34
namnium1125

総合スコア2043

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

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

reo517

2020/04/27 19:31

理解しました。ありがとうございます。まったく動きが分かってませんでした。。。
guest

0

蟻本は、アマゾンの試し読みで読めました。

if (i == n) return sum == k;

// n個決め終わったら、sumがkと等しいかを返す
★sumが等しいかを返すならtrue or falseではないのですか?
★returnで何が返ってくるのかわかりません

== は、2項演算子で、
第1項と第2項が等しければ、演算結果はtrue。
第1項と第2項が等しくなければ、演算結果は false です。
sum と k が等しければ、true を返し、
sum と k が等しくなければ、false を返します。

if (dfs(0, 0)) printf("Yes\m");

★なぜ0,0なんですか?

bool dfs(int i, int sum) { と定義された関数 dfs は、
a[0]~a[i-1] の部分和が sum に入っているとき、
a[i] を加えない場合と加える場合の 2通りについて、
残りの a[i+1], ... a[n-1] の部分和を sum に加えたものが
k に等しければ true を返し、等しくなければ false を返します。

dfs(0, 0) で dfs を呼び出すと、i = 0, sum = 0 で呼び出すので、
a[0]~a[n-1] の部分和を sum に加えたものが k に等しければ true、
そうでなければ false が返ってきます。

if (dfs(i+1, sum)) return true;

a[i] を加えない場合、残りの a[i+1]~a[n-1] の部分和を sum に
加えたものが k に等しいかどうかが返ってくるので、
true が返ってきたら、探索終了で true で返ります。

if (dfs(i+1, sum + a[i])) return true;

a[i] を加えた場合、残りの a[i+1]~a[n-1] の部分和を sum に
加えたものが k に等しいかどうかが返ってくるので、
true が返ってきたら、探索終了で true で返ります。

a[i] を部分和に加えても加えなくても false だったら、
a[0]~a[i-1] の部分和が間違っていたことになるので
false で戻って、違う組み合わせの部分和に変更してもらいます。

投稿2020/04/27 18:38

kazuma-s

総合スコア8224

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

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

reo517

2020/04/27 19:25

2項演算子の質問はすみません。精進します。読み込んで理解します。夜中に返信ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.46%

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

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

質問する

関連した質問