SRM 460 250pt: TheQuestionsAndAnswersDivOne

問題概要

質問がQ(<=9)問存在する。全ての質問が最低1回は行われた。回答のリストがN(<=9)個与えられる。同じ質問には同じ回答がなされている。可能な質問の順番は何通りあるか求める問題。

考えたこと

  • 解けそうで解けない。制限が小さいから全探索っぽいことをするんだろうけど。
  • yesと答えた質問がいくつあるか、を固定すれば場合の数に落ちそう。
  • でもこれは何度も考えてその度に解けなかったやつだ。
  • 具体的には、N個のボールをK色のいずれかで塗る。ただしどの色も最低1回は使っている。そのような組み合わせは何通りあるか。
  • 包除原理使えば一応出せると思うけど、包除原理とか間違えやすいので書きたくない。
  • なので組み合わせ的な考えは最後の手段として置いておき、ビットDPの解法を考える。
  • 状態を(i番目の質問、どの質問がyesか、どの質問が既に使われたか)でいけそう?
  • これだと(どの質問がyesか)っていうのは独立なので外に出せる(出さなくても別にいいけど)。
  • なので、どの質問がyesか固定した状態で(i番目の質問、どの質問が既に使われたか)でDP行ける。
  • 書けた。サンプル通らない。何で?
  • NとQ間違えてたというかQ使って無かった。修正して提出。無事通った。

practiceで通ったコード

計算量O(N * Q * 2^(2*Q))。

#include <vector>
#include <string>
#include <cstring>
using namespace std;

const int MAX_N = 9;
int dp[MAX_N+2][1<<MAX_N];

struct TheQuestionsAndAnswersDivOne {

	int find(int questions, vector <string> answers) {
		const int N = answers.size();

		int ret = 0;
		for(int is_yes=0; is_yes<(1<<questions); is_yes++){
			memset(dp, 0, sizeof(dp));

			dp[0][0] = 1;
			for(int i=0; i<N; i++){
				bool yes = answers[i] == "Yes";
				for(int bit=0; bit<(1<<questions); bit++){
					for(int j=0; j<questions; j++)if( yes ^ !((is_yes>>j)&1) ){
						dp[i+1][bit | (1<<j)] += dp[i][bit];
					}
				}
			}

			ret += dp[N][(1<<questions)-1];
		}

		return ret;
	}

};