Codeforces 279D (171D) The Minimum Number of Variables

問題

n項からなる数列aが与えられる。
m個の変数bに対して次のような操作をn回行う。

  • 最初、全ての変数は0
  • t回目の操作で、b[y] := b[i] + b[j]とbを更新する。このとき、b[i] + b[j] = a[t]でなくてはならない。


この操作が行える変数の数の最小値を求めよ。
変数がいくつあっても操作を行えないとき、-1を出力せよ。

制約条件

a[i]は全て互いに異なる。
a[i]≦10^9
n≦23

方針

i番目の操作まで行ったとき、変数のなかに残っているaの集合をbitとするときの、
変数の数の最小値をdp[i][bit]とする。
これを更新するようなDPをする。


状態数は2^nで、
それぞれに対してi番目の操作が行えるかを判定するのを上手くやるとn、
消す数を選ぶのにnかかるので、
全体でO(2^n n)の計算量がかかる。


判定を上手くやるのは、a[i]が互いに全て異なるということを使う。
a[i] = a[j] + a[k]となるi, j, kは、i, jを決めれば一通り以下に定まる。
したがって、jをn通り探すだけで、状態がvalidかどうか判定できる。

ソースコード

メモリを23*2^23じゃなくて2^23に節約してある。

int n, a[23], dp[1 << 23], p[23][23];

int main(){
	cin >> n;
	rep(i, n) cin >> a[i];
	rep(i, 1 << n) dp[i] = inf;
	dp[1] = 1;
	
	memset(p, -1, sizeof(p));
	rep(i, n) rep(j, n) rep(k, n) if(a[k] == a[i] + a[j]) p[k][i] = j;
	
	for(int i = 1; i < n; i++) rep(j, 1 << i) if((j & 1 << i - 1) && dp[j] < inf){
		bool ok = 0;
		rep(k, i) if(j & 1 << k){
			int t = p[i][k];
			if(t >= 0 && (j & 1 << t)){
				ok = 1;
				break;
			}
		}
		if(!ok) continue;
		dp[j ^ 1 << i] = min(dp[j ^ 1 << i], dp[j] + 1);
		rep(k, i) if(j & 1 << k) dp[j ^ 1 << i ^ 1 << k] = min(dp[j ^ 1 << i ^ 1 << k], dp[j]);
	}
	int ans = inf;
	rep(i, 1 << n) if(i & 1 << (n - 1)) ans = min(ans, dp[i]);
	cout << (ans == inf ? -1 : ans) << endl;
	
	return 0;
}