AOJ 1620 Boolean Expression Compressor
解法
前もってすべての式を生成してしまえばOK。
真理値表は 16bit 分の情報があればよく、bitDP 的な要領で生成していけば良い。
つまり、キーが真理値表で、値がその最小長さである。
生成するのにはダイクストラが楽だと思う。
ダイクストラで遷移するときは、今見ている式(これは真理値表と長さのペアのことを指している)を、これまでに作ったすべての式と組み合わせて新しい式を作る。
これまでに作ったすべての式を見る部分の計算量がヤバそう(式としてありえるのが 2^16 個あるから)だが、式の長さが高々16文字までしか使えないことを考えると、実は生成できる真理値表はそもそもかなり少ない。
なので、なんかいい感じに作ろうと頑張らなくてもサボって通せる。
ソースコード
#include <bits/stdc++.h> using namespace std; using pii = pair<int, int>; constexpr int inf = 1e9; int eval_impl(string const& s, int& p, const int S) { if(s[p] == '0' || s[p] == '1') { return s[p++] == '1'; } if(isalpha(s[p])) { return (S >> (s[p++] - 'a')) & 1; } if(s[p] == '-') { return !eval_impl(s, ++p, S); } if(s[p] == '(') { const bool t1 = eval_impl(s, ++p, S); const char op = s[p++]; const bool t2 = eval_impl(s, p, S); p++; // ) if(op == '^') return t1 ^ t2; else return t1 & t2; } assert(false); return -1; } int eval(string const& s) { int truth = 0; for(int S = 0; S < 1 << 4; ++S) { int p = 0; truth |= (eval_impl(s, p, S) << S); } return truth; } int main() { constexpr int all_mask = (1 << 16) - 1; vector<int> ans(1 << 16, inf); ans[0] = ans[(1 << 16) - 1] = 1; ans[eval("a")] = ans[eval("b")] = ans[eval("c")] = ans[eval("d")] = 1; vector<int> ts = {0, (1 << 16) - 1, eval("a"), eval("b"), eval("c"), eval("d")}; vector<int> len = {1, 1, 1, 1, 1, 1}; priority_queue<pii, vector<pii>, greater<>> que; for(int i = 0; i < (int)ts.size(); ++i) { que.emplace(len[i], ts[i]); } while(!que.empty()) { const auto l = que.top().first; const auto t = que.top().second; que.pop(); if(ans[t ^ all_mask] > l + 1) { ans[t ^ all_mask] = l + 1; que.emplace(l + 1, t ^ all_mask); ts.push_back(t ^ all_mask), len.push_back(l + 1); } const int m = ts.size(); for(int i = 0; i < m; ++i) { if(len[i] + l + 3 > 16) continue; if(ans[t & ts[i]] > len[i] + l + 3) { ans[t & ts[i]] = len[i] + l + 3; ts.push_back(t & ts[i]), len.push_back(len[i] + l + 3); que.emplace(len[i] + l + 3, t & ts[i]); } if(ans[t ^ ts[i]] > len[i] + l + 3) { ans[t ^ ts[i]] = len[i] + l + 3; ts.push_back(t ^ ts[i]), len.push_back(len[i] + l + 3); que.emplace(len[i] + l + 3, t ^ ts[i]); } } } string exp; while(cin >> exp, exp != ".") { const auto t = eval(exp); cout << ans[t] << endl; } }
感想
一昨年は結局本番で通せなかったなあ。
他の人のコードを読むと頑張って賢く生成しようとしていて大変そうだった。
とりあえず愚直生成で遅かったら考える、ぐらいの気持ち(運良く早くてよかった)。