TopCoder SRM 547 Div2 Hard RelativelyPrimeSubset
問題
n個の正の整数S[i]が与えられる。
この整数から、部分集合sを、sのどの二つの要素も互いに素であるように選びたい。
sの要素の数の最大値はいくつか、求めよ。
制約条件
S[i]≦100
S[i]は互いに異なる
n≦50
方針
Sの互いに素でない要素を辺で結んだグラフを考えると、
求めるのはこのグラフの最大独立集合。
なのだけど、最大独立集合をO(2^sqrt(2m) * n)で求めたらTLEだった。
Sの上限が小さいことを使う。
S[i]が50以上の素因数を持っていたら、
その素因数が公約数になることはないので、その素因数を無視してよい。
すると、素因数は2〜47の15個に限られるので、
dp[i][j]を、i番目の要素まで見て、今素因数をjで表される集合だけ使ったときの
要素の最大値として、これを更新していくようなDPをすればよい。
ソースコード
TLEしたやつ(O(2^sqrt(2m) n^2))
計算量をO(2^sqrt(2m) * n)に落として、自明に採用できるノードを採用することにしたら通るような気がしなくもない。
後でやってみる。
int max_clique(vector<vi> &e){ int n = e.size(), deg[50] = {}; int m = 0; rep(i, n) rep(j, n) if(e[i][j]) deg[i]++, m++; m /= 2; int ans = -1; rep(i, n) if(deg[i] * deg[i] < 2 * m){ int v[50], t = 0; rep(j, n) if(e[i][j]) v[t++] = j; rep(j, 1 << t){ rep(k, t) rep(l, k) if((j & 1 << k) && (j & 1 << l) && !e[v[k]][v[l]]) goto FAIL; ans = max(ans, __builtin_popcount(j) + 1); FAIL:; } vector<vi> nxt = e; nxt.erase(nxt.begin() + i); rep(j, n - 1) nxt[j].erase(nxt[j].begin() + i); ans = max(ans, max_clique(nxt)); break; } if(ans < 0){ rep(i, 1 << n){ rep(j, n) rep(k, j) if((i & 1 << j) && (i & 1 << k) && !e[j][k]) goto FAIL2; ans = max(ans, __builtin_popcount(i)); FAIL2:; } } ans = max(ans, 1); return ans; } class RelativelyPrimeSubset { public: int findSize(vector <int> S) { int n = S.size(); vector<vi> e(n, vi(n)); rep(i, n) rep(j, n) e[i][j] = i != j && __gcd(S[i], S[j]) == 1; return max_clique(e); } };
計算量O(n^sqrt(2m) n)に落として、自明な頂点は必ず使うようにした版。
結局TLEだった。
vector<vi> e; int n, m, deg[50]; bool use[50]; int max_clique(){ int ans = -1; rep(i, n) if(use[i] && deg[i] * deg[i] < 2 * m){ int v[50], t = 0; rep(j, n) if(use[j] && e[i][j]) v[t++] = j; vector<bool> ok(1 << t); ok[0] = 1; rep(j, 1 << t) if(j){ int k = 0; for(; !(j & 1 << k); k++); if(!ok[j ^ 1 << k]) goto FAIL; rep(l, t) if(k != l && (j & 1 << l) && !e[v[k]][v[l]]) goto FAIL; ans = max(ans, __builtin_popcount(j) + 1); ok[j] = 1; FAIL:; } use[i] = 0; rep(j, t) deg[v[j]]--, m--; ans = max(ans, max_clique()); break; } if(ans < 0){ int v[50], t = 0; rep(i, n) if(use[i]) v[t++] = i; vector<bool> ok(1 << t); ok[0] = 1; rep(i, 1 << t) if(i){ int k = 0; for(; !(i & 1 << k); k++); if(!ok[i ^ 1 << k]) goto FAIL2; rep(j, t) if(j != k && (i & 1 << j) && !e[v[j]][v[k]]) goto FAIL2; ans = max(ans, __builtin_popcount(i)); FAIL2:; } if(t) ans = max(ans, 1); else ans = max(ans, 0); } return ans; } class RelativelyPrimeSubset { public: int findSize(vector <int> S) { int ans = 0; rep(i, S.size()){ bool ok = 1; rep(j, S.size()) if(i != j && __gcd(S[i], S[j]) != 1) ok = 0; if(ok){ ans++; S.erase(S.begin() + i--); } } n = S.size(); m = 0; e = vector<vi>(n, vi(n)); rep(i, n) deg[i] = 0, use[i] = 1; rep(i, n) rep(j, i) if(__gcd(S[i], S[j]) == 1){ e[i][j] = e[j][i] = 1; m++; deg[i]++; deg[j]++; } ans += max_clique(); return ans; } };