题意
传送门 LeeCode 1896 反转表达式值的最少操作次数
题解
根据布尔表达式建立一颗括号树,具体而言,出现嵌套括号就新增一个虚拟子节点。建树过程可以用一个栈维护。此括号树满足各个节点的子节点集合是按照计算顺序排列且可以直接计算的节点。最后在树上以表达式取值为状态进行树形DP即可。总时间复杂度 O ( ∣ e x p ∣ ) O(\lvert exp\rvert) O(∣exp∣)。
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
struct Edge {
char op;
int to;
};
int minOperationsToFlip(string expression) {
vector<vector<Edge>> g(1);
vector<int> vs(1, -1);
vector<int> par(1, 0);
auto add = [&](int x) {
int v = g.size();
g.push_back({});
vs.push_back(x);
return v;
};
for (auto c : expression) {
if (c == ')') {
par.pop_back();
} else if (c == '(') {
int v = add(-1);
g[par.back()].push_back({' ', v});
par.push_back(v);
} else if (c == '0' || c == '1') {
int v = add(c - '0');
g[par.back()].push_back({' ', v});
} else {
g[par.back()].push_back({c, -1});
}
}
int sz = vs.size();
const int inf = 1e9;
vector<vector<int>> dp(sz, vector<int>(2, inf));
function<void(int)> get = [&](int v) {
if (vs[v] != -1) {
int x = vs[v];
dp[v][x] = 0;
dp[v][x ^ 1] = 1;
return;
}
char op = ' ';
for (auto [o, u] : g[v]) {
if (u == -1) {
op = o;
continue;
}
get(u);
if (op != '&' && op != '|') {
dp[v][0] = dp[u][0];
dp[v][1] = dp[u][1];
} else {
vector<int> tmp(2, inf);
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < 2; ++j) {
int sum = dp[v][i] + dp[u][j];
if (op == '&') {
tmp[i & j] = min(tmp[i & j], sum);
tmp[i | j] = min(tmp[i | j], sum + 1);
} else {
tmp[i | j] = min(tmp[i | j], sum);
tmp[i & j] = min(tmp[i & j], sum + 1);
}
}
}
swap(dp[v], tmp);
}
}
};
get(0);
return max(dp[0][0], dp[0][1]);
}
};