🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

C++

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

Q&A

解決済

2回答

788閲覧

全探索のdpでの実装をしたい

PC_breakman

総合スコア30

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

C++

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

0グッド

1クリップ

投稿2021/02/15 07:23

前提・実現したいこと

AOJのALDS1_5_Aの問題をdpで解こうと思い実装したのですが、vectorのサイズオーバフローのエラーが出ました。なぜ出るのか全く分からないので、教えていただきたいです。問題の内容は、入力4行目の各数について、入力2行目の数をいくつか足し合わせて作ることができるか判定せよ、というものです。

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

terminate called after throwing an instance of 'std::out_of_range' what(): vector::_M_range_check: __n (which is 18446744073709551597) >= this->size() (which is 2005) Aborted (core dumped)

入力例

5 1 5 7 10 21 4 2 4 17 8

期待される出力例

no no yes yes

該当のソースコード

C++

1#include <bits/stdc++.h> 2using namespace std; 3 4#define MAX 2005 5vector<vector<int>> dp(25, vector<int> (MAX, -1)); // dp配列 6 7int n, q; 8vector<int> a(MAX); 9 10int solve(int i, int m) { 11 if(dp.at(i).at(m) != -1) return dp.at(i).at(m); // 該当の部分が計算済みならそれを返す 12 13 if(m == 0) { // m == 0なら可能 14 dp.at(i).at(m) = 1; 15 }else if(i >= n) { // i >= nなら不可 16 dp.at(i).at(m) = 0; 17 }else if(solve(i + 1, m) == 1) { 18 dp.at(i).at(m) = 1; 19 }else if(solve(i + 1, m - a.at(i)) == 1) { 20 dp.at(i).at(m) = 1; 21 }else{ 22 dp.at(i).at(m) = 0; 23 } 24 return dp.at(i).at(m); 25} 26 27int main() { 28 cin >> n; 29 for(int i = 0; i < n; i++) cin >> a.at(i); 30 cin >> q; 31 for(int i = 0; i < q; i++) { 32 int m; 33 cin >> m; 34 if(solve(0, m)) cout << "yes" << endl; 35 else cout << "no" << endl; 36 } 37 return 0; 38}

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

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

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

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

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

guest

回答2

0

C++

1 }else if(solve(i + 1, m - a.at(i)) == 1) {

ここで、ma.at(i)より小さいと、solveの引数mがマイナスになります。
そうすると、solveの先頭でマイナスのインデックスにアクセスしようとしてエラーになります。


追記

mがマイナスならメモ化もせずに返るようにすれば修正できます。

diff

1 int solve(int i, int m) { 2+ if(m < 0) return 0; 3 if(dp.at(i).at(m) != -1) return dp.at(i).at(m); // 該当の部分が計算済みならそれを返す

mがマイナスの時にorimで登録するのは誤りです。
今回のコードだと実行順の関係で問題が発生することはありませんが、
solve(i+1,m-a.at(i),orim)側を先に呼び出すようになっていたら、正常動作しないパターンが出てきます。

text

12 24 3 31 43

投稿2021/02/15 11:10

編集2021/02/16 20:46
actorbug

総合スコア2429

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

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

PC_breakman

2021/02/16 14:35

原因をくださってありがとうございます。無事解決できました。
PC_breakman

2021/02/18 01:46

追記ありがとうございます。 mが書き換えられてしまうからということでしょうか。
actorbug

2021/02/18 12:42

コメントからすると、何か勘違いされているように思います。 リンク先の問題(英語版)にsolveの呼び出し階層の図が書いてあると思いますが、 私が提示した「正常動作しないパターン」で同じ図を書いてみてください。 そして、どの位置でmがマイナスになるか、そこでdp.at(i).at(orim)に0を書き込むのが本当に正しいか 確認してみてもらえませんか。
guest

0

自己解決

下記のように、多少強引ですが、再帰処理によって時々刻々変化するmの値をもう一つ引数として保持しておくことで解決できました。ほかに何かいい方法があったら是非教えてください。

C++

1int solve(int i, int m, int orim) { 2 if(m >= 0 && dp.at(i).at(m) != -1) return dp.at(i).at(m); 3 4 if(m == 0) { 5 return dp.at(i).at(m) = 1; 6 }else if(i >= n || m < 0) { 7 if(m < 0) m = orim; 8 return dp.at(i).at(m) = 0; 9 } 10 int res = solve(i + 1, m, orim) || solve(i + 1, m - a.at(i), orim); 11 if(res) { 12 dp.at(i).at(m) = 1; 13 }else{ 14 dp.at(i).at(m) = 0; 15 } 16 return dp.at(i).at(m); 17} 18 19int main() { 20 cin >> n; 21 for(int i = 0; i < n; i++) cin >> a.at(i); 22 cin >> q; 23 for(int i = 0; i < q; i++) { 24 int m; 25 cin >> m; 26 if(solve(0, m, m)) cout << "yes" << endl; 27 else cout << "no" << endl; 28 } 29 return 0; 30}

投稿2021/02/16 14:38

PC_breakman

総合スコア30

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問