AIZU ONLINE JUDGE

AOJ ALDS1 5_A(Exhaustive Search)を解く

AOJ_ALDS1_5_A

Aizu Online Judge(AOJ)が提供している「アルゴリズムとデータ構造入門」(ALDS1)の5_A問題をC++で解いてみました。

ALDS1では、「アルゴリズムとデータ構造の基礎を体得します。」とあります。トピック#5「分割統治法」では、部分的な小さな問題を解くことによって、与えられた元の問題を解く手法を紹介します。

問題(5_A: Exhaustive Search)

問題はリンク先をご覧ください。

AOJ ALDS1 5_A問題: Exhaustive Search

再帰関数を使う全探索について学びます。

再帰関数を使う全探索について

この問題は、数列の長さとなる $n$ 重ループを書けば解くことができます。再帰関数を使えば、回数が決まっていないループ相当の処理を実現することができます。

C++ プログラム例(ALDS1 5_A)

再帰関数 rec は、以下のように実装します(8ー18行目)。

  • 数列をどこまで処理したかを表す i と、総和の残り remain を引数とします。総和が m になる組み合わせがあれば true を返します。そうでなければ false を返します。
  • 引数 remain が 0 になれば、総和となる組み合わせが見つかったことを表します(10、11行目)。
  • すべて調べて総和となる組み合わせがなければ false を返します(13、14行目)。
  • 再帰的に関数を呼び出して、a[i] を選択する場合と選択しない場合を調べます(17行目)。

関数 rec の戻り値によって、”yes” または “no” を出力します。以下が、C++プログラムとなります。

#include <iostream>
#include <vector>
using namespace std;

int n;
vector<int> a;

bool rec(int i, int remain)
{
	if (remain == 0) {
		return true;
	}
	if (i == n) {
		return false;
	}

	return rec(i + 1, remain) || rec(i + 1, remain - a[i]);
}

int main()
{
	cin >> n;
	a.resize(n);
	for (int i = 0; i < n; ++i) {
		cin >> a[i];
	}

	int q;
	cin >> q;
	for (int i = 0; i < q; ++i) {
		int m;
		cin >> m;
		if (rec(0, m)) {
			cout << "yes" << endl;
		} else {
			cout << "no" << endl;
		}
	}

	return 0;
}

上のプログラムでは、$n$ 重ループの替わりに再帰関数を使って解答を求めました。

$2^n$ 通りのループを行う bit全探索という手法があります。bit全探索については、この記事で説明しています。bit全探索で総和となる値をすべて set コンテナ sum に格納します。読みだした値が sum に含まれているか判定して、結果を出力します。

以下が、bit全探索を使ったプログラムとなります。キーとなるコードの背景行を変えています。

#include <iostream>
#include <vector>
#include <set>
using namespace std;

typedef unsigned int uint;

int main()
{
	int n;
	cin >> n;
	vector<int> a(n);
	for (int i = 0; i < n; ++i) {
		cin >> a[i];
	}

	set<int> sum;
	for (uint bit = 0; bit < (1ULL << n); ++bit) {
		int t = 0;
		for (uint i = 0; i < n; ++i) {
			if ((bit & (1ULL << i)) != 0) {
				t += a[i];
			}
		}
		sum.insert(t);
	}

	int q;
	cin >> q;
	for (int i = 0; i < q; ++i) {
		int m;
		cin >> m;
		if (sum.find(m) != sum.end()) {
			cout << "yes" << endl;
		} else {
			cout << "no" << endl;
		}
	}

	return 0;
}

上記プログラムはどちらも、AOJ で「AC(Accepted=正解)」と判定されました。

最後に

トピック#5では、再帰関数を使って、部分的な小さな問題に分解して解く手法を紹介します。

引き続き、ALDS1 の問題を紹介していきます。

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA